开发者

How to implement template class covariance in C++?

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 add开发者_StackOverflowitional 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.


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).


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜