Search code examples
c++c++11templatessfinaeenable-if

How to disable a class member function for certain template types


It seems to be simple, but I have some difficulties with the syntax of std::enable_if

The situation is actually quite simple.

a template class with template parameter T

2 functions which shall not be implemented for one specific type of T.

Both functions have no parameters or return values of T

One function accepts an int and the other function returns an int.

Any simple example ?

Or is there another option (C++11) which does not use std::enable_if ?


Solution

  • It's really simple. Just remember to use another template parameter that is defaulted to the class/struct template parameter.

    Suppose you want a class foo<T> with two members, void foo<T>::bar1 (int) and int foo<T>::bar2 () and suppose that you want that bar1() and bar2() are implemented only if T is different from long.

    You can do as follows

    #include <type_traits>
    
    template <typename T>
    struct foo
     {
       template <typename U = T>
       typename std::enable_if<false == std::is_same<U, long>::value>::type
          bar1 (int)
           { }
    
       template <typename U = T>
       typename std::enable_if<false == std::is_same<U, long>::value, int>::type
          bar2 ()
           { return 0; }
     };
    
    int main()
     {
       foo<int>  fi;
       foo<long> fl;
    
       fi.bar1(0); // compile
       fi.bar2();  // compile
    
       // fl.bar1(0); // compilation error
       // fl.bar2();  // compilation error
     }
    

    There is a danger: someone can bypass your control and explicit the U type as follows

    foo<long> fl;
    
    fl.bar1<long long>(0);
    

    To avoid this problem, you can improve your std::enable_if test as follows

       template <typename U = T>
       typename std::enable_if
             <sizeof(U) && (false == std::is_same<T, long>::value)>::type
          bar1 (int)
           { }
    
       template <typename U = T>
       typename std::enable_if
             <sizeof(U) && (false == std::is_same<T, long>::value), int>::type
          bar2 ()
           { return 0; }
    

    If you can use a C++14 compiler, using std::enable_if_t you can avoid a couple of typename and a couple if ::type and semplify the code as follows

       template <typename U = T>
       std::enable_if_t<sizeof(U) && (false == std::is_same<T, long>::value)>
          bar1 (int)
           { }
    
       template <typename U = T>
       std::enable_if_t<sizeof(U) && (false == std::is_same<T, long>::value), int>
          bar2 ()
           { return 0; }
    

    As of C++20, the requires keyword makes things much simpler:

       void bar1 (int) requires (!std::is_same_v<T, long>)
           { }
    
       int bar2 () requires (!std::is_same_v<T, long>)
           { return 0; }