wesolyromek wesolyromek - 29 days ago 22
C++ Question

How to subclass a superclass chosen by traits?

I have a template of a class (let's call it A) that should subclass one of the other classes (B or C), depending on a compile time conditional in my custom trait struct. I'm attaching a snippet that reproduces the behaviour.

#include <type_traits>

template<typename T>
class cls_template {
public:
using method_arg_type = T;
virtual void method(method_arg_type) = 0;
};

using A = cls_template<float>;
using B = cls_template<int>;

template<typename T>
struct traits {
using cls = std::conditional<std::is_floating_point<T>::value, A, B>;
};

//class C : public traits<float>::cls {
class C : public A {
public:
virtual void method(method_arg_type arg) {};
};

int main() {
A *a = new C();
}


If I leave it like this (hardcoding A as superclass) it all works fine. However, once I replace the class definition with a commented out line I'm getting the following error messages:

test.cpp:21:27: error: unknown type name 'method_arg_type'
virtual void method(method_arg_type arg) {};
^
test.cpp:25:10: error: cannot initialize a variable of type 'A *'
(aka 'cls_template<float> *') with an rvalue of type 'C *'
A *a = new C();
^ ~~~~~~~


Why is
method_arg_type
no longer defined? Why is
C
no longer recognised as subclass of
A
? I've found out that if
traits
aren't a template (if I just hardcode a type into the struct), it all works fine.

Answer

You're trying to derive from traits<float>::cls which is of type std::conditional<std::is_floating_point<T>::value, A, B>. It's neither A nor B, it's a specialization of the conditional template. Either derive from ::type of it, which will work as you expected, or use conditional_t (C++14).

class C : public traits<float>::cls::type { // ok
                                   ^^^^^^
public:
  virtual void method(method_arg_type arg) {};
};

demo

template<typename T>
struct traits {
  using cls = std::conditional_t<std::is_floating_point<T>::value, A, B>;
                              ^^
  // above is C++14, in C++11 you'd have to write
  // using cls = typename std::conditional<std::is_floating_point<T>::value, A, B>::type;
};

class C : public traits<float>::cls { // also ok
public:
  virtual void method(method_arg_type arg) {};
};

demo