C++ - Is there a way for a template class specialization to contain code from the general template?

Viewed 529

Given a C++ class:

template<typename T>
class A {
public:
    int a;
    T x;
    int getA() {return a;}
};

Is it possible for a template specialization to 'inherit' (without actual C++ inheritance) members like a and getA() from A? Furthermore, when writing code that does a lot of template specializations for classes, should I type the same code many times (which kinda defeats the whole purpose of templates), or restructure the class so that it accommodates for the specializations (e.g. by encapsulating another template class member inside so that specializations are limited to that class only)?

3 Answers

Is it possible for a template specialization to 'inherit' (without actual C++ inheritance) members like a and getA() from A? 

Short answer: no.

Long answer: no, A is a class template and thus it's not something from which you can inherit members (either data or functions).
Moreover, what you are asking for sounds like - how can I defeat the whole purpose of a specialization?. Well, go with inheritance from a base class for members that don't depend on template parameters. That's the more straightforward way to do that and what the language offers you.

You cannot just inherit a member function from a type without extending that type.
There a lot of techniques to achieve the purpose: direct inheritance, mixin, inherit-from-above idiom and so on.
All of them go through inheritance to compose interfaces. I don't see why one should demonize it.

If you don't want additional classes then you can trick a little bit like in:

template <class T>
class A {
public:
        void f() { cout << "here" << endl; }
};

template <> class A<char> : private A<A<char> >  {
public:
        using A<A<char> >::f;
};

Of course one would like to be able to write template <> class A<char> : private A<char> and obtain a specialization that inherits from the generic case, but it is not possible, so I used a trick to instanciate the generic case with some type (here something like CRTP but one can use any other type).

Another trick would be to use a second type and a default value.

I found a way:

#include <type_traits>

typedef void**const*const** dummy;

// Generic template
template <typename T1, typename T2 = dummy>
class A {
    static_assert(std::is_same<T1,dummy>::value || std::is_same<T2,dummy>::value,
                  "too many template arguments for class template 'A'");
    typedef typename std::conditional<std::is_same<T1,dummy>::value, T2, T1>::type T;
    public:
        int a;
        T x;
        int getA() {return a;}
};

// Specialization for int
template <typename X>
class A<int,X> : public A<X,int> {
    public:
        void foo() {}
};

Class template A is instantiated with exactly one template argument. When A is instantiated with any type other than int, it has all of the members of the generic, as expected. When A is instantiated with int, it has not only the members of the specialization, but also the members of the generic.

Related