How to implement template class covariance in C++?

守給你的承諾、 提交于 2019-12-21 22:27:57

问题


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.


回答1:


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




回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/4908392/how-to-implement-template-class-covariance-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!