15

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
eddi
  • 49,088
  • 6
  • 104
  • 155
  • @AndyG I'd get "A,B,C" if I didn't specialize. I want the specialization for A and B. – eddi Aug 14 '17 at 17:05
  • 1
    This looks like a duplicate of this: https://stackoverflow.com/questions/12858839/using-sfinae-for-template-class-specialisation If it isn't a duplicate then it is a close relative and your answer is probably there...gcc 4.9.2 and all – Dr t Aug 14 '17 at 17:12
  • @Drt as I mention in the OP - that question was my starting point - I'm just not sure how to adapt it for my needs. My current attempt does not work with 4.9.2. – eddi Aug 14 '17 at 17:15
  • 1
    @eddi Btw there's `std::conditional` if your example is not really contrived - you don't need to specialize in that case. – LogicStuff Aug 14 '17 at 17:16
  • @LogicStuff unfortunately I do need to specialize, as `SomeClass` is used in a different class (which I again cannot change) to figure out what type to use in some complicated factory construction logic. – eddi Aug 14 '17 at 17:18
  • Do you have lots of subclasses of `A`, so you definitely can't just explicitly specialize for `A`, `B`, ... ? – Useless Aug 14 '17 at 18:07
  • @Useless I've been explicitly specializing until I got to 3 subclasses, and that's when the desire for a more universal solution kicked in. I'm expecting more subclasses in the future. – eddi Aug 14 '17 at 18:09
  • You're not allowed to change `SomeClass`, but are you allowed to change code like `SomeClass::Type`? :-) Because I think that's the only way it'll work. – AndyG Aug 14 '17 at 18:39
  • @AndyG basically the only thing I can do is specialize `SomeClass` - I can't change it, or change how it's going to be used. – eddi Aug 14 '17 at 19:06
  • @eddi - and you can't add a default template parameter to `SomeClass`? – max66 Aug 14 '17 at 19:09
  • @max66 correct, the class and its usage are in a read-only library; I only get the specialization as a knob to turn if I want to change the behavior of some other library functionality – eddi Aug 14 '17 at 19:14
  • You can't change `SomeClass`, right. Can you change the classes `A`, `B`, `C` instead? – skypjack Aug 14 '17 at 21:53

1 Answers1

3

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.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • Thanks, this is interesting. I have access to modify class A, but I'm pretty sure I'd get murdered for doing this :) It is an educational approach though. – eddi Aug 15 '17 at 17:53
  • 1
    @eddi Well, as I said, it works. That's the sole advantage. Beautifulness isn't another one. :-D – skypjack Aug 15 '17 at 19:44