Reputation: 379
I am just trying my hands on SFINAE using std::enable_if
in C++. I thought i understood the theory part until i couldn't get the following piece of code to compile. Adding to this confusion is the different behaviour on Visual studio and Linux. This pasted code compiles on VS as long as you don't uncomment (Calculator<int> cInt;
). However, using GCC it gives me compilation error. I already see this kind of code in STL implementations, i was really expecting more standardized implementations everywhere. Anyways, can you please check and suggest what are the gaps in my understanding here?
template<typename T>
class Calculator
{
public:
typename enable_if<is_arithmetic<T>::value, T>::type
addition(T a, T b)
{
return a + b;
}
typename enable_if<!is_arithmetic<T>::value, T>::type
addition(T a, T b)
{
cout << "Default\n";
return a;
}
};
void SFINAE()
{
// Calculator<int> cInt;
}
int main ()
{
SFINAE();
return 0;
}
Error log with GCC 8.1: j
doodle.cpp:30:3: error: 'typename std::enable_if<(! std::is_arithmetic<_Tp>::value), T>::type Calculator<T>::addition(T, T)' cannot be overloaded with 'typename std::enable_if<std::is_arithmetic<_Tp>::value, T>::type Calculator<T>::addition(T, T)'
addition(T a, T b)
^~~~~~~~
jdoodle.cpp:25:3: note: previous declaration 'typename std::enable_if<std::is_arithmetic<_Tp>::value, T>::type Calculator<T>::addition(T, T)'
addition(T a, T b)
Error log on VS when you uncomment Calculator class initialization with int:
sfinae.h(17): error C3646: 'addition': unknown override specifier
sfinae.h(17): error C2059: syntax error: '('
sfinae.h(18): error C2334: unexpected token(s) preceding '{'; skipping apparent function body
Upvotes: 5
Views: 1161
Reputation: 66230
In a class, SFINAE works for template methods and over template parameters of the method.
So
typename enable_if<is_arithmetic<T>::value, T>::type
addition(T a, T b)
{
return a + b;
}
can't works because you're trying to apply SFINAE over a method that isn't template and a test (is_arithmetic<T>::value
) that is over the template parameter of the class.
You should try something as
template <typename U = T>
typename enable_if<is_arithmetic<U>::value, T>::type
addition(T a, T b)
{
return a + b;
}
This way the template become a template one with a template parameter (U
) with a
default type (T
) and you have the SFINAE test over the template parameter of the method.
The same for the other addition()
method.
To avoid that someone "hijack" your code explicating a wrong template parameter
Calculator<std::string> cs;
cs.add("a", "b"); // call the Default version
cs.template add<int>("a", "b"); // call the arithmetic version!!!
you can impose that U
and T
are the same type
template <typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value
&& std::is_same<T, U>::value, T>::type
addition(T a, T b) // ^^^^^^^^^^^^^^^^^^^^^^^^^
{
return a + b;
}
Upvotes: 8