Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
}
like image 974
Steve Lorimer Avatar asked Dec 11 '14 21:12

Steve Lorimer


2 Answers

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

like image 96
Praetorian Avatar answered Sep 23 '22 15:09

Praetorian


Whilst not solving the problem of why the incorrect operator was not discarded, to solve the particular issue at hand, that is, to return by const ref for class types or by value for others, a solution can be found using std::conditional.

template< bool B, class T, class F >
struct conditional;

Provides member typedef type, which is defined as T if B is true at compile time, or as F if B is false.

Working example:

#include <iostream>
#include <type_traits>

template<typename T>
struct Foo
{
    operator typename std::conditional<
        std::is_class<T>::value, const T&, 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;
}
like image 44
Steve Lorimer Avatar answered Sep 24 '22 15:09

Steve Lorimer