How to implement template class covariance in C++?

3.2k Views Asked by At

Is it possible to implement a class template in such a way that one object could be casted to another if their template arguments are related? Here is an exaple to show the idea (of course it will not compile):

struct Base {};
struct Derived : Base {};

template <typename T> class Foo {
    virtual ~Foo() {}
    virtual T* some_function() = 0;
};

Foo<Derived>* derived = ...;
Foo<Base>* base = derived;

The additional problem here is that Foo is an abstract class used as an interface containing functions returning T& and T*, so I can't implement a template copy constructor.

I'm writing a universal Iterator class which can hold any STL iterator, and in addition to type erasure I'd like it to be polymorphic, i.e. I could write something like this:

std::list<Derived> l;
MyIterator<Base> it(l.begin());

UPD: That was my mistake, I didn't actually need casting Foo* to Foo* to implement MyIterator, so I think the question is not actual anymore.

3

There are 3 best solutions below

4
On BEST ANSWER

The template argument has nothing to do with the content of the object you are pointing to. There is no reason this should work. To illustrate

struct Base { };
struct Derived : Base {};

template<typename T> struct A { int foo; };
template<> struct A<Base> { int foo; int bar; };

A<Derived> a;
A<Base> *b = &a; // assume this would work
b->bar = 0; // oops!

You will eventually access integer bar that doesn't really exist in a!


OK, now that you provided some more information, it's clear you want to do something completely different. Here is some starter:

template<typename T>
struct MyIterator : std::iterator<...> {
  MyIterator():ibase() { }
  template<typename U>
  MyIterator(U u):ibase(new Impl<U>(u)) { }
  MyIterator(MyIterator const& a):ibase(a.ibase->clone())

  MyIterator &operator=(MyIterator m) {
    m.ibase.swap(ibase);
    return *this;
  }

  MyIterator &operator++() { ibase->inc(); return *this; }
  MyIterator &operator--() { ibase->dec(); return *this; }
  T &operator*() { return ibase->deref(); }
  // ...

private:
  struct IBase { 
    virtual ~IBase() { }
    virtual T &deref() = 0; 
    virtual void inc() = 0;
    virtual void dec() = 0;
    // ...

    virtual IBase *clone() = 0;
  };
  template<typename U>
  struct Impl : IBase { 
    Impl(U u):u(u) { }
    virtual T &deref() { return *u; }
    virtual void inc() { ++u; }
    virtual void dec() { --u; }
    virtual IBase *clone() { return new Impl(*this); }
    U u;
  };

  boost::scoped_ptr<IBase> ibase;
};

Then you can use it as

MyIterator<Base> it(l.begin());
++it; 
Base &b = *it;

You may want to look into any_iterator. With a bit of luck, you can use that template for your purpose (I haven't tested it).

0
On

Foo<Derived> doesn't inherit Foo<Base>, so you can't convert the former to the latter. Also, your assumption is wrong: dynamic_cast will fail.

You could create a new object of instance of Foo<Base> that copies your Foo<Derived> instance, but I guess this is now what you're looking for.

3
On

While the other answers have pointed that this kind of relationship isn't "built-in" with templates, you should note that it is possible to build functionality to work with this sort of relationship. For example, boost::shared_dynamic_cast and friends given

class A { ... };
class B : public A { ... };

let you cast between boost::shared_ptr<A> and boost::shared_ptr<B>.

Note that if you do go about implementing something like this, you'll have to be careful about the operations that MyIterator supports. For example, using your example of MyIterator<Base>(std::list<Derived>::iterator), you should not have an lvalue version of operator*(), eg,

  *myIter = someBaseValue;

should not compile.