'Cannot be overloaded' error while trying to enable SFINAE with enable_if
Asked Answered
C

1

5

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
Congo answered 16/9, 2019 at 21:39 Comment(0)
E
8

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;
}
Ethanethane answered 16/9, 2019 at 21:45 Comment(1)
To avoid someone overriding template argument deduction you can also do something like template <int&... ExplicitArgumentBarrier, typename U = T> (which is what abseil does).Bigoted

© 2022 - 2024 — McMap. All rights reserved.