I'm writing a template class that stores a std::function
in order to call it later. Here is the simplifed code:
template <typename T>
struct Test
{
void call(T type)
{
function(type);
}
std::function<void(T)> function;
};
The problem is that this template does not compile for the void
type because
void call(void type)
becomes undefined.
Specializing it for the void
type doesn't alleviate the problem because
template <>
void Test<void>::call(void)
{
function();
}
is still incompatible with the declaration of call(T Type)
.
So, using the new features of C++ 11, I tried std::enable_if
:
typename std::enable_if_t<std::is_void_v<T>, void> call()
{
function();
}
typename std::enable_if_t<!std::is_void_v<T>, void> call(T type)
{
function(type);
}
but it does not compile with Visual Studio:
error C2039: 'type' : is not a member of 'std::enable_if'
How would you tackle this problem?
Specialize the whole class:
template <>
struct Test<void>
{
void call()
{
function();
}
std::function<void()> function;
};
SFINAE doesn't work over (only) the template parameters of the class/structs.
Works over template methods whit conditions involving template parameters of the method.
So you have to write
template <typename U = T>
std::enable_if_t<std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<!std::is_void<U>::value> call(T type)
{ function(type); }
or, if you want to be sure that U
is T
template <typename U = T>
std::enable_if_t< std::is_same<U, T>::value
&& std::is_void<U>::value> call()
{ function(); }
template <typename U = T>
std::enable_if_t<std::is_same<U, T>::value
&& !std::is_void<U>::value> call(T type)
{ function(type); }
p.s.: std::enable_if_t
is a type so doesn't require typename
before.
p.s.2: you tagged C++11 but your example use std::enable_if_t
, that is C++14, and std::is_void_v
, that is C++17
If you don't stick to use void
, and your intention is to actually able to use Test
without any parameters, then use variadic template:
template <typename ...T>
struct Test
{
void call(T ...type)
{
function(type...);
}
std::function<void(T...)> function;
};
This way, you can have any number of parameters. If you want to have no parameters, use this:
Test<> t;
t.call();
So this is not exactly the syntax you wanted, but there is no need for specialization, and this solution is more flexible, because supports any number of parameters.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With