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

How to disable member functions using std::enable_if


What is the differences between these classes? Specifically, these member functions with enable_if.

/// Alias of std::enable_if...
template <bool B, typename T = void>
using Enable_if = typename std::enable_if<B, T>::type;

template<typename T, std::size_t N>
class A {
   ...
    template <std::size_t NN = N,
          typename = Enable_if<NN == 2>>
    Some_Return_Type
    method(param1, param2)
    {}

    template <std::size_t NN = N,
              typename = Enable_if<NN == 1>>
    Some_Return_Type
    method(param1)
    {}

};


template<typename T, std::size_t N>
class B {
   ...
    Enable_if<N == 2, Some_Return_Type>
    method(param1, param2)
    {}

    Enable_if<N == 1, Some_Return_Type>
    method(param1)
    {}
};

What is the correct way to use enable_if in the case I have:

  • At least 2 methods that differs only in their arguments and they have the same name but just one of them must be "active" (if N == 1 one of them, if N == 2 the other).
  • Only one method that will be active if N == 0 and not active in other cases.

I would like to get something like:

Obj<int, 2> obj2;
Obj<int, 0> obj0;
Obj<int, 1> obj1;

if I write in the IDE obj0. it should show only methods of:

  • N == 0;
  • for obj1., only N == 1
  • ...

Solution

  • Note that enable_if is aimed to trigger SFINAE: if a template parameter substitution fails in its immediate context, it is not a compilation error.

    This is exaclty what happens in class A: when the user calls a.method(...), the compiler attempts to instantiate member function template method, subsituting NN parameter with a constant, which might fail.

    However, in case of B::method the "bad" substitution occurs during class template B instantiation, when the compiler substitues N. The failure occurs far from the parameter's immediate context, which is in this case template<typename T, std::size_t N> class B.

    That's why in the second case you will get a compilation error, rather than SFINAE.

    So, to enable/disable member functions depending on the class template parameter, use the first approach, combining conditions as you wish. For instance:

    template <typename T, std::size_t N>
    class A {
        template <std::size_t NN = N, typename = std::enable_if_t<NN == 2 || NN == 0>>
        void method(int, int)
        {}
    
        template <std::size_t NN = N, typename = std::enable_if_t<NN == 1 || NN == 0>>
        void method(int)
        {}
    };
    

    UPDATE: How enable_if works. Roughly, one can implement it like this:

    template<bool, class T = void>
    struct enable_if {};
    
    template<class T>
    struct enable_if<true, T> {
        using type = T;
    };
    

    Note that if the first argument is false, enable_if doesn't have inner type, so enable_if<false, int>::type would be ill-formed. That's what triggers SFINAE.