Search code examples
c++c++11sfinaetemplate-aliases

C++: Trying to encapsulate std::enable_if in class


I was just using SFINAE to select function templates when I had the glorious idea to encapsulate std::enable_if in a struct like this

template<typename T, typename U>
struct EnableWhenEqual
{
    typedef typename std::enable_if<std::is_same<T, U>::value>::type type;
};

and then use it, for example, like this

template<typename T, typename U, typename Enable = EnableWhenEqual<T,U>::type >
void foo(T&& t, U&& u)
{
    if (std::is_same<T,U>::value)
        std::cout << "OK, overload allowed." << std::endl;
    else
        std::cout << "Bad. Should not compile!" << std::endl;
}

However, this doesn't work, as one sees by invoking

foo(1,1);     //prints "OK, overload allowed"
foo(1,2.0);   //prints "Bad, should not compile", but obviously does

On the other hand, by trying to create

EnableWhenEqual<int,int>();      //compiles
EnableWhenEqual<int,double>();   //does not compile because type does not exist

one obtains a compiler error ("type is not a member of std::enable_if").

What is the reason for this behaviour? I'm asking because from my little SFINAE knowledge I would have thought that an error in the type-deduction leads to exclusion of the overload ... ?


For completeness, the above problem can be solved using template aliasing

template <typename T, typename U>
using EnableWhenEqual = typename std::enable_if<std::is_same<T,U>::value>::type;

Is there also an alternative using a struct instead of a template alias?

EDIT: Here I mean an implementation which solves the general problem. For example, this here

template<typename T, typename U, bool B, typename C=void>
struct EnableWhenEqual {};

template<typename T, typename U, typename C>
struct EnableWhenEqual<T,U,std::is_same<T,U>::value>
{
    typedef typename C type;
};

don't works for me because the partial specialization needs a simple identifier. If it would, one could replace std::is_same by general structs Condition<T,U>. Any alternatives?


Solution

  • This should do the trick:

    template<typename T, typename U>
    struct EnableWhenEqual : std::enable_if<std::is_same<T, U>::value>
    {
    };
    

    http://coliru.stacked-crooked.com/a/650202ba3d42d34b