Search code examples
c++template-meta-programmingenable-if

Unable to discriminate template specialization with enable_if and is_base_of


I am trying to trade some run-time checks for compile-time checks to identify the base class of an object with template specializations.

The code compiles fine, but I can't figure out why the enable_if statement always end up invalid or equal to void because I'm always landing on the base template struct.

#include <iostream>
#include <type_traits>
using namespace std;

struct BaseOne {};
struct DerivedOne : BaseOne {};
struct BaseTwo {};
struct DerivedTwo : BaseTwo {};
struct Default {};

template<typename T, typename = void>
struct get_category_impl {
    static constexpr int value = 0;
};

template<typename T>
struct get_category_impl<T, typename enable_if<is_base_of<BaseOne, T>::value, T>::type> {
    static constexpr int value = 1;
};

template<typename T>
struct get_category_impl<T, typename enable_if<is_base_of<BaseTwo, T>::value, T>::type> {
    static constexpr int value = 2;
};

template<typename T>
constexpr int get_category = get_category_impl<T>::value;

int main() {
    cout << get_category<BaseOne>    << "\n"; // prints 0
    cout << get_category<DerivedOne> << "\n"; // prints 0
    cout << get_category<BaseTwo>    << "\n"; // prints 0
    cout << get_category<DerivedTwo> << "\n"; // prints 0
    cout << get_category<Default>    << "\n"; // prints 0
}

Solution

  • The second parameter to enable_if doesn't need to be specified. If you do specify it, it needs to somehow resolve to void. Since you've specified the second parameter as T, this doesn't work.

    Instead, just do this:

    template<typename T>
    struct get_category_impl<T, typename enable_if<is_base_of<BaseOne, T>::value>::type> {
                                                                             // ^  No T 
        static constexpr int value = 1;
    };
    

    and similarly for the other specialization.

    Here's a demo.