如何在C ++中实现模板类协方差?

时间:2011-02-05 17:33:11

标签: c++ templates inheritance covariance contravariance

是否可以以这样的方式实现类模板:如果一个对象的模板参数相关,则可以将一个对象转换为另一个对象?这是一个显示这个想法的exaple(当然它不会编译):

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

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

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

这里的另一个问题是Foo是一个抽象类,用作包含返回T&amp; S的函数的接口。和T *,所以我无法实现模板复制构造函数。

我正在编写一个通用的Iterator类,它可以容纳任何STL迭代器,除了类型擦除之外我还希望它是多态的,即我可以写这样的东西:

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

UPD:这是我的错误,我实际上并不需要将Foo *转换为Foo *来实现MyIterator,所以我认为这个问题已经不再存在了。

3 个答案:

答案 0 :(得分:4)

模板参数与您指向的对象的内容无关。这没有理由这样做。为了说明

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!

您最终将访问bar中确实不存在的整数a


好的,既然您提供了更多信息,很明显您想要做一些完全不同的事情。这是一些启动者:

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;
};

然后你可以用它作为

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

您可能需要查看any_iterator。运气好的话,您可以将该模板用于您的目的(我还没有测试过)。

答案 1 :(得分:4)

虽然其他答案指出这种关系并非“内置”模板,但您应该注意,可以构建功能来处理这种关系。例如,boost::shared_dynamic_cast and friends给出

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

让您在boost::shared_ptr<A>boost::shared_ptr<B>之间进行投射。

请注意,如果你真的要实现这样的东西,你必须要小心MyIterator支持的操作。例如,使用MyIterator<Base>(std::list<Derived>::iterator)的示例,您不应该有operator*()的左值版本,例如,

  *myIter = someBaseValue;

不应该编译。

答案 2 :(得分:1)

Foo<Derived>不会继承Foo<Base>,因此您无法将前者转换为后者。此外,您的假设是错误的:dynamic_cast将失败。

您可以创建一个Foo<Base>实例的新对象来复制您的Foo<Derived>实例,但我想这就是您正在寻找的内容。