Search code examples
listc++11templatesstdenable-if

`std::enable_if` SFINAE custom type via template parameter?


First question: let's say that I have a custom struct like this one:

struct Var {};   

struct is_var_type: std::integral_constant<bool, std::is_same<Var, typename std::remove_cv<T>::type>::value> {};

After this, I want to have some custom type via a template parameter SFINAE using std::enable_if, however I couldn't figure it out. I could only do this:

template<typename T> 
void variant(T a)
{ 
    if (is_var_type<Var>::value){
        std::cout << " " << true;
    }
    else {
        std::cout << " " << false;  
    }
}

I want to make something like this one, but std::is_integral has already been defined. How is a custom one made?

template<class T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> // I couldn't figure out how to make custom integral_constant via template parameter 
T foo3(T t) // note, function signature is unmodified
{
    return t; //so this type should be an int, but I want to make sure that this type T is is_var_type struct, is this possible?
}

Second question: is this way possible if we add some container type like this?

struct Var {};
struct ListVar: public std::list<Var>
struct is_var_type: std::integral_constant<bool, std::is_same<ListVar, typename std::remove_cv<T>::type>::value> {};

– but I got an even more confusing compile time error, something like cannot deduce multiple type in gcc 4.9.


Solution

  • I would make is_var_type a template alias wrapping std::is_same. A solution could look like this.

    #include <type_traits> // for std::decay
    #include <iostream>
    
    struct Var {};
    
    // define is_var_type
    template<class T>
    using is_var_type = std::is_same<Var, typename std::decay<T>::type >;
    
    // function that works for Var
    template<class T
           , typename std::enable_if<is_var_type<T>::value>::type* = nullptr
           >
    T foo3(T t)
    {
       std::cout << " T is type Var " << std::endl;
       return t;
    }
    
    // function that works for int
    template<class T
           , typename std::enable_if<std::is_same<T,int>::value>::type* = nullptr
           >
    T foo3(T t)
    {
       std::cout << " T is type int " << std::endl;
       return t;
    }
    
    int main()
    {
       Var v;
       foo3(v); // calls foo3 for type Var
    
       int i;
       foo3(i); // calls foo3 for type int
       return 0;
    }
    

    You can also define is_var_type using std::integral_constant like you did in your example (you were missing template<class T> before your definition):

    template<class T>
    struct is_var_type:std::integral_constant<bool,std::is_same<Var,typename std::decay<T>::type>::value > 
    {
    };
    

    EDIT:

    For a container of Var it is the same story as above, now we just check whether T is of type ListVar:

    // define structure that is a list of Var
    struct ListVar : public std::list<Var>
    {
    };
    
    // struct that checks whether T is a ListVar
    template<class T>
    struct is_list_of_var_type : std::integral_constant<bool,std::is_same<ListVar, typename  std::remove_cv<T>::type>::value > {};
    
    // function that only works for ListVar
    template<class T
           , typename std::enable_if<is_list_of_var_type<T>::value>::type* = nullptr
           >
    T foo3(T t)
    {
       std::cout << " T is type ListVar " << std::endl;
       return t;
    }
    
    // ... some code ...
    ListVar lv;
    foo3(lv); // call foo3 for ListVar