具有模板容器的协变返回类型

时间:2011-05-11 20:08:24

标签: c++ templates inheritance

是否可以进行以下工作?基本上我想要Ptr< B>成为Ptr< A>的可接受的返回类型替代。

template<typename T>
class Ptr {
public:
    explicit Ptr (T * ptr) : ptr(ptr) {}
    T * ptr;
};


class A {
    virtual Ptr<A> foo () {
        return Ptr<A>(NULL);
    }
};

class B : public A {
    virtual Ptr<B> foo () { // BAD! Ptr<B> is not compatable
        return Ptr<B>(NULL);
    }
};

3 个答案:

答案 0 :(得分:4)

您可以使用奇怪的重复模板来替换模板函数返回的虚函数重载。以下文章可能有所帮助:

http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

同时检查以下代码:

template <class C_>
class A_Base {
    Ptr<C_> foo() {
        return static_cast<C_*>(this)->foo_impl();
    }
}

class A : public A_Base<A> {
    Ptr<A> foo_impl() { return Ptr<A>(NULL); }
}

class B : public A_Base<B> {
   Ptr<B> foo_impl() { return Ptr<B>(NULL); }
}

答案 1 :(得分:1)

根据标准:

重写函数的返回类型应与重写函数的返回类型相同或与函数类的协变相同。如果函数D :: f重写函数B :: f,则函数的返回类型如果满足以下条件则是协变的:

  • 都是指向类的引用或类的引用
  • B :: f的返回类型中的类与D :: f的返回类型中的类相同,或者是D的返回类型中类的明确的直接或间接基类: :f,可在D
  • 中访问
  • 指针或引用具有相同的cv限定,并且返回类型D :: f中的类类型具有与b ::的返回类型中的类类型相同的cv-qualification或更少的cv-qualification F。

因此,在您的示例中,返回类型不是真正的协变(它们不是指针也不是引用),而且Ptr<B>Ptr<A>是不相关的类型。

因此,保持foo虚拟并在A中返回Ptr<A>并且在B中返回Ptr<B>是不可能的。如果你能够/愿意放弃虚拟,你可以使用Cem Kalyoncu的提议或其中的变体。

答案 2 :(得分:0)

您可以尝试这样的事情:

template<class T> class Ptr
{
...
};

template<class T> class A
{
    virtual Ptr<T> foo();
};

class B : public A<B>
{
    virtual Ptr<B> foo();
};