I have a template class (that I cannot modify), let's call it SomeClass
, that I'd like to specialize for classes that derive from a particular class only. Following this answer I was able to do this in gcc 6.3.1, but unfortunately I need to do it in gcc 4.9.2, and there it fails at compile time saying "partial specialization SomeClass<T>
does not specialize any template arguments".
Is there any way I could change the below to make it work with gcc 4.9.2?
#include <iostream>
#include <string>
using namespace std;
struct A {
string name() { return "A"; }
};
struct B : A {
string name() { return "B"; }
};
struct C {
string name() { return "C"; }
};
template<typename T, typename = std::enable_if_t<std::is_base_of<A, T>::value>>
using enable_if_a = T;
template<typename T>
struct SomeClass {
using Type = T;
};
template<typename T>
struct SomeClass<enable_if_a<T>>
{
using Type = A;
};
int main(int, char**)
{
SomeClass<A>::Type el1;
SomeClass<B>::Type el2;
SomeClass<C>::Type el3;
cout << el1.name() << "," << el2.name() << "," << el3.name() << endl;
}
Output:
A,A,C
A template is not a class or a function.
It is possible to inherit from a template class. All the usual rules for inheritance and polymorphism apply. If we want the new, derived class to be generic it should also be a template class; and pass its template parameter along to the base class.
This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming. Generic programming is an approach where generic data types are used as parameters in algorithms so that they work for variety of suitable data types.
Explicit (full) specializationAllows customizing the template code for a given set of template arguments.
A bit contrived, but here is a machinery that at least works.
The basic idea is to hide A
and do not inherit directly from it. Instead, you can heavily rely on mixins and combine a few classes in a detector with which you can specialize SomeClass
.
The drawback is that classes like B
become more abstruse and I'm not sure it's worth it at the end of the day. Direct specializations are probably better.
That being said, here is a working example:
#include <iostream>
#include <string>
#include <utility>
using namespace std;
class ADerivedFactory {
struct A {
string name() { return "A"; }
};
template<typename T>
struct Detector: T { using type = A; };
public:
template<template<typename> class C>
using type = Detector<C<A>>;
};
template<typename T>
struct AT : T {};
template<typename T>
struct BT : T {
string name() { return "B"; }
};
using A = ADerivedFactory::type<AT>;
using B = ADerivedFactory::type<BT>;
struct C {
string name() { return "C"; }
};
template<typename T>
struct SomeClass {
using Type = T;
};
template<template<typename> class C>
struct SomeClass<ADerivedFactory::type<C>>
{
using Type = typename ADerivedFactory::type<C>::type;
};
int main(int, char**)
{
SomeClass<A>::Type el1;
SomeClass<B>::Type el2;
SomeClass<C>::Type el3;
cout << el1.name() << "," << el2.name() << "," << el3.name() << endl;
}
See it up and running on wandbox.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With