Ludovicio
Ludovicio

Reputation: 135

C++ Templates, Polymorphism, and Template Covariance

I have a "wrapper" template that looks like this:

template <typename T>
class Wrapper {
  virtual T* get() = 0;
}

Then I have the following inheritance scheme:

class A {
  // things
}

class B : public A {
  // more things
}

I have another class that uses that wrapper:

class Worker {
  void work(Wrapper<A>* wa){
    A* a = wa->get();
    //do stuff with a.
  }
}

All that can be done with A*, can be done with B*, but i cannot pass a Wrapper<B>* as a Wrapper<A>* to Worker. Is there a way to allow this?

I have read about C++ concepts, but I don't see how they might solve the problem.

Upvotes: 3

Views: 485

Answers (3)

fish2000
fish2000

Reputation: 4435

How about a bit of the Curiously Recurring Template Pattern:

class SubBase {
    virtual T* get() { /* default impl. */ }
};

template <typename T>
class Base : public SubBase {};

template <typename T>
class Wrapper : public Base<T> {
    virtual T* get() = 0;
}

… I don’t know what your inheritance graph looks like, but as long as your wrapped T types don’t have a get() method, this should work.


EDIT: this simple snippet doesn’t work, nor is it really CRTP, as has been pointed out; I could only make an example of this sort work after fairly significant modification.

Upvotes: -1

Daniel
Daniel

Reputation: 31559

If each class reports its base:

class A {
  using Base = void;
  // things
}

class B : public A {
  using Base = A;
  // more things
}

You could do

template<typename T>
class Wrapper : public EmptyForVoid<T::Base>::Type

And use EmptyForVoid for the base class selection:

template<typename T>
struct EmptyForVoid { using Type = T; };

template<>
struct EmptyForVoid<void> { struct Type {}; };

This will make Wrappers follow the same inheritance tree as the types they wrap.

Upvotes: 4

Nir Friedman
Nir Friedman

Reputation: 17704

You can't do it directly, because C++ does not have covariance. In C++ where methods are not virtual by default (unlike Java, and I'm guessing C#), it's not clear if this would be an awesome idea (and the language is already very complicated). Basically what you have to do is have a thin templated wrapper on work:

class Worker {
public:
  template <class T>
  void work(Wrapper<T>* wa){
    work_on_a(wa->get());
  }
private:
  void work_on_a(A* a) { // do stuff with a}
}

This is still safe as this will only compile if the wrapped type is derived from A or otherwise defines an implicit conversion to A, otherwise the function call to work_on_a will fail.

You can do more sophisticated things here with various pros and cons, but this keeps the code as simple as possible, and minimizes template bloat.

Although note, that if work also needs to be virtual, this will not work (since virtual functions cannot be templates), and things will get hairy fast.

Another way to go, is to argue that the wrapper should not be passed into work at all; if all you need to do work is a pointer to A, then that's all you should pass in. But depending on your real code that may not be an option, or not make sense.

Upvotes: -1

Related Questions