Search code examples
c++templatessfinaetype-traitsenable-if

enable conversion operator using SFINAE


I'm trying to overload operator T() using SFINAE to return a copy when T is a fundamental type, and a const reference when T is a class.

When using a double in my example below, I can't get the 2nd overload (with std::is_class) to be removed.

That is, the error I'm getting is:

error: no type named ‘type’ in ‘struct std::enable_if<false, const double&>’
operator typename std::enable_if< std::is_class<T>::value, const T&>::type () const
^

What am I doing wrong?

#include <iostream>
#include <type_traits>

template<typename T>
struct Foo
{
    operator typename std::enable_if<!std::is_class<T>::value, T >::type () const
    {
        return _val;
    }

    operator typename std::enable_if< std::is_class<T>::value, const T&>::type () const
    {
        return _val;
    }

    T _val;
};

int main()
{
    Foo<double> f1;
    f1._val = 0.3;

    double d = f1;
    std::cout << d << std::endl;
    return 0;
}

Solution

  • T is already known at the time your class member functions are instantiated, so no substitution occurs, and instead of SFINAE, you get a hard error. The easiest workaround is to introduce a dummy template parameter for those operator overloads and default it to T so that type deduction can still occur.

    template<typename U = T>
    operator typename std::enable_if<!std::is_class<U>::value, U >::type () const
    {
        return _val;
    }
    
    template<typename U = T>
    operator typename std::enable_if< std::is_class<U>::value, const U&>::type () const
    {
        return _val;
    }
    

    Live demo