Home > Net >  std::conditional - Invalid parameter type ‘void’ even when testing for 'void'
std::conditional - Invalid parameter type ‘void’ even when testing for 'void'

Time:05-26

The following compiles on Visual Studio:

template<typename ArgType, typename ReturnType>
struct Test
{
    using FunctionPointerType = std::conditional_t<
        std::is_same_v<ArgType, void>
        , ReturnType(*)()
        , ReturnType(*)(ArgType)
    >;
    FunctionPointerType Func;
};

int main()
{
    Test<void, char> tt;
}

But does not compile on Linux g . The error I get is

error : invalid parameter type ‘void’

I know I cannot use void in templates, which is why I have used std::conditional_t and std::is_same_v.

I cannot see what is incorrect, can someone please tell me?

CodePudding user response:

Not really an answer to your question (Why doesn't my code work), but this works:

template<typename ArgType, typename ReturnType>
struct Test
{
  using FunctionPointerType = ReturnType(*)(ArgType);
  FunctionPointerType Func;
};

template<typename ReturnType>
struct Test<void, ReturnType>
{
  using FunctionPointerType = ReturnType(*)();
  FunctionPointerType Func;
};

CodePudding user response:

template<typename ArgType, typename ReturnType>
struct Test
{
  using FunctionPointerType = std::conditional_t<std::is_same_v<ArgType, void>, ReturnType(*)(), ReturnType(*)(ArgType)>;
  FunctionPointerType Func;
};

conditional_t takes 3 arguments. A bool and 2 types.

The right type, when ArgType is void, is ReturnType(*)(void) -- not a legal type.

The error happens immediately.

MSVC has a bad habit of treating templates like macros.

Something like this:

template<class...Args>
struct Apply {
  template<template<class...>class Z>
  using To = Z<Args...>;
};

template<class R, class...Args>
using FunctionPtr = R(*)(Args...);

template<typename ArgType, typename ReturnType>
struct Test
{
  using FunctionPointerType =
    std::conditional_t<
      std::is_same_v<ArgType, void>,
      Apply<ReturnType>,
      Apply<ReturnType, ArgType>
    >::template To<FunctionPtr>;
  FunctionPointerType Func;
};

builds 2 pack appliers, then applies one to FunctionPtr depending on which one is picked.

Here, I avoid forming X(void) types until after the condition branch has occurred.

CodePudding user response:

In std::conditional_t<B, T, F> both the true and false specialization should have valid types T as well as F. In your case, since the F deduce to an invalid char(*)(void) type, std::conditional can not be used here.

I would suggest a helper traits function_ptr_t as alternative

#include <type_traits>

template<typename RetType, typename... Args> struct function_ptr final {
    using type = RetType(*)(Args...);
};

template <typename RetType> struct function_ptr<RetType, void> final {
    using type = RetType(*)();
};
// alias helper
template<typename RetType, typename... Args>
using function_ptr_t = typename function_ptr<RetType, Args...>::type;


template<typename RetType, typename... Args>
struct Test
{
     function_ptr_t<RetType, Args...> Func;
};

See a demo


Note that, I have swapped the template arguments in the class template Test, and made the second template argument be a variadic, so that the Test<char> will result in a member function pointer of type char(*)().

That means the following will work:

Test<char> tt1;
static_assert(std::is_same_v<char(*)(), decltype(tt1.Func)>, " are not same!");

Test<char, void> tt2;
static_assert(std::is_same_v<char(*)(), decltype(tt2.Func)>, " are not same!");

Test<void, double> tt3;
static_assert(std::is_same_v<void(*)(double), decltype(tt3.Func)>, " are not same!");

If that is not, you wanted, substitute the variadic template arguments to be a normal one.

  • Related