wesolyromek
wesolyromek

Reputation: 650

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.

Upvotes: 3

Views: 169

Answers (1)

krzaq
krzaq

Reputation: 16431

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

Upvotes: 4

Related Questions