Search code examples
c++c++14enable-if

constructor overloading and SFINAE


As an exercise in understanding the usage of std::enable_if I tried implementing a wrapper class (struct) to represent a particular type at any given point in time:

#include<type_traits>
#include<typeinfo>
#include<iostream>
using std::enable_if;
using std::is_same;
using std::cout;
using std::endl;

template<typename T>
struct type_wrap{

                 type_wrap(typename enable_if<is_same<int,T>::value,T>::type&& rrT):value(rrT){
                         cout << "The wrapped type is " << typeid(value).name() << endl;
                         cout << "The wrapped value is " << value << endl;
                } 
                 type_wrap(typename enable_if<is_same<float,T>::value,T>::type && rrT):value(rrT){
                         cout << "The wrapped type is " << typeid(value).name() << endl;
                         cout << "The wrapped value is " << value << endl;
                 }

                 T& value;
};

int main(){

        type_wrap<int>(0);
        type_wrap<float>(0.5);
        return(0);
}

The above code does not compile:

so_main.cpp:16:47: error: no type named 'type' in 'std::__1::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration
                 type_wrap(typename enable_if<is_same<float,T>::value,T>::type && rrT):value(rrT){
                                              ^~~~~~~~~~~~~~~~~~~~~~~
so_main.cpp:26:9: note: in instantiation of template class 'type_wrap<int>' requested here
        type_wrap<int>(0);
        ^
so_main.cpp:12:47: error: no type named 'type' in 'std::__1::enable_if<false, float>'; 'enable_if' cannot be used to disable this declaration
                 type_wrap(typename enable_if<is_same<int,T>::value,T>::type&& rrT):value(rrT){
                                              ^~~~~~~~~~~~~~~~~~~~~
so_main.cpp:27:9: note: in instantiation of template class 'type_wrap<float>' requested here
        type_wrap<float>(0.5);
        ^
2 errors generated.

The code works if I were to remove one of the overloaded constructors, and the corresponding instantiation from main(). But that defeats the whole purpose of this exercise.

Can someone point out the cause for the compilation error?


Solution

  • SFINAE works on template method(/constructor), here it is your class which is template, you might use the following (even if specialization seems simpler/better in your case):

    template<typename T>
    struct type_wrap{
        template <typename U,
                  std::enable_if_t<std::is_same<int, U>::value
                                   && is_same<int, T>::value>* = nullptr>
        type_wrap(U arg) : value(arg){
            // Int case
            std::cout << "The wrapped type is " << typeid(value).name() << std::endl;
            std::cout << "The wrapped value is " << value << std::endl;
        }
    
        template <typename U,
                  std::enable_if_t<std::is_same<float, U>::value
                                   && is_same<float, T>::value>* = nullptr>
        type_wrap(U arg) : value(arg){
            // float case
            std::cout << "The wrapped type is " << typeid(value).name() << std::endl;
            std::cout << "The wrapped value is " << value << std::endl;
        }
        T value;
    };
    

    Demo