Search code examples
c++templatestype-traitsenable-if

std::enable_if to choose a class specialitzation


I'm tyring to understand std::enable_if and the benefits of using it over a static_assert / regular template specialitzation.

After reading around I found:

This is useful to hide signatures on compile time when a particular condition is not met, since in this case, the member enable_if::type will not be defined and attempting to compile using it should fail. http://www.cplusplus.com/reference/type_traits/enable_if/

My question then is: Why the compiler blames me by saying that class C is already declared?, when only one of the declaraions should be avaiable at a time.

class Interface{};

class Foo : public Interface{};

template <class T, typename = typename std::enable_if<std::is_base_of<Interface,T>::value>::type>
class C{
   // Some custom implementation
}

template <class T, typename = typename std::enable_if<!std::is_base_of<Interface,T>::value>::type>
class C { 
  //Some fallback / default implementation
}

int main() {
    C<Foo> myVariable;
}

Same behaviour in Godbolt: https://godbolt.org/z/cbfhG9q54

Thanks in advance!


Solution

  • You cannot overload class templates like you can function templates, buy you can partially specialize them (which you cannot do with function templates):

    #include <ios>
    #include <iostream>
    #include <type_traits>
    
    class Interface
    {};
    class Foo : public Interface
    {};
    
    template <class T, typename = void>
    struct C
    {
        // Some default impl.
        static constexpr bool is_default_impl{true};
    };
    
    template <class T>
    struct C<T, std::enable_if_t<std::is_base_of_v<Interface, T>>>
    {
        // Some custom implementation.
        static constexpr bool is_default_impl{false};
    };
    
    int main()
    {
        std::cout << std::boolalpha 
            << C<int>::is_default_impl << " "  // true
            << C<Foo>::is_default_impl;        // false
    }
    

    Note that this examples requires C++17 for the variable template std::is_base_of_v which is a short-hand constant the value member of the std::is_base_of trait, and C++14 for the alias template std::enable_if_t, which aliases the type member alias declaration of the std::enable_if trait.