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;
};
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.