Search code examples
c++templatesc++11template-specializationenable-if

C++ Template specialization for subclasses with abstract base class


Let's say I have a pure abstract base class. A class template implements this interface, and is specialized. Now, my issue is that this specialization should be able to handle subclasses of the specialization. So, I tried enable_if, but then the subclass ends up being abstract... How can I get around this?

By example:

// This example doesn't work because a subclass of A does not satisfy the
// specialization for B<T>::foo()
class A {
public:
    virtual void foo() = 0;
};

template <class T>
class B : public A {
    ...
public:
    ...
    void foo();
    ...
};

void B::foo() {
    ...
}

template <>
void B<A>::foo() {
    ...
}

class C : A {
    ...
public:
    ...
    void foo();
    ...
};

int main() {
    B<C> bar;  // I was like "this is gonna work!! :) :D"
    bar.foo(); // But this calls B::foo() instead of B<A>::foo()... :'( *sob*
}

And another example:

// This example doesn't work because B ends up being abstract
class A {
public:
    virtual void foo() = 0;
};

template <class T>
class B : public A {
    ...
public:
    ...
    template <class U=T>
    typename std::enable_if<!std::is_base_of<U, A>::value, void>::type
    foo() {
        ...
    }

    template <class U=T>
    typename std::enable_if<std::is_base_of<U, A>::value, void>::type
    foo() {
        ...
    }
};

class C : A {
    ...
public:
    ...
    void foo();
    ...
};

int main() {
              // I got excited thinking this would work \(^.^)/
    B<C> bar; // and then it didn't because B is abstract /(-_-)\ ...
    bar.foo();
}

Any ideas on how to solve this? Thanks!!


Solution

  • B<C> and B<A> are distinct types, so your first case will never work.

    What you want to do is to specialize the template for all classes T for which std::is_base_of<A, T>::value is true. For that, use a default template parameter with partial specialization:

    template <class T, bool = std::is_base_of<A, T>::value>
    class B : public A {
    public:
        void foo() override { std::cout << "A is not base of T!" << std::endl; }
    };
    
    
    template <class T>
    class B<T, true> : public A {
    public:
        void foo() override { std::cout << "A is base of T!" << std::endl; }
    };
    

    When A is a base of T, the bool parameter is true so the partial specialization is used. Otherwise, the base template is used.

    Note that is_base_of will return true even if A is an inaccessible base of T, so you may also want to add a is_convertible<T*, A*> check.

    Demo.