c++c++11templatesspecializationenable-if

C++ partial template specialization in combination with std::is_base_of and std::enable_if


Let's say I have a two classes: Serializable and Printable.

So a simple template function which accepts all derived classes of Printable could look like:

template <class T, class B = Printable, class = typename std::enable_if<std::is_base_of<B,     T>::value>::type>
void print(T value) {
    cout << value << endl;
}

However, if I want it to accept also all derived classes of Serializable while I still have control over the function body, this would obviously not work:

template <class T, class B = Printable, class = typename std::enable_if<std::is_base_of<B,     T>::value>::type>
void print(T value) {
    cout << value << endl;
}

template <class T, class B = Serializable, class = typename std::enable_if<std::is_base_of<B,     T>::value>::type>
void print(T value) {
    cout << value << endl;
}

// Error: Redefinition of ...

So I figured the remaining solutions for this problem are template specializations.

But I just can't figure out, how I can specialize a template in combination with std::is_base_of and std::enable_if.

I hope someone is willing to help me!


Solution

  • Try a logical operator:

    std::enable_if<std::is_base_of<Serializable, T>::value ||
                   std::is_base_of<Printable, T>::value>::type
    

    You can easily write a variadic template like:

    is_base_of_any<T, Printable, Serialiable, Googlable, Foobarable>::value
    

    For example:

    template <typename T, typename ...> struct is_base_of_any : std::true_type {};
    
    template <typename T, typename Head, typename ...Rest>
    struct is_base_of_any<T, Head, Rest...>
    : std::integral_constant<bool, std::is_base_of<T, Head>::value ||
                                   is_base_of_any<T, Rest...>::value>
    { };
    

    If you want different implementations:

    template <bool...> struct tag_type {};
    
    template <typename T>
    void foo(T, tag_type<true, false>) { }   // for Printable
    
    template <typename T>
    void foo(T, tag_type<false, true>) { }   // for Serializable
    
    template <typename T>
    void foo(T x)
    {
        foo(x, tag_type<std::is_base_of<Printable, T>::value,
                        std::is_base_of<Serializable, T>::value>());
    }
    

    The last overload (the "user-facing" one) should probably be endowed with the above enable_if to not create overly many overload candidates.

    You can probably also make a variadic template <typename ...Bases> with a tag like:

    tag_type<std::is_base_of<Bases, T>::value...>