CRTP和动态多态编译错误

时间:2013-03-22 12:29:06

标签: c++ templates polymorphism standards crtp

class A {
    virtual A* foo() = 0;
};

template<class T>
class B : public A {
    virtual T* foo() { return nullptr; }
};

class C : public B<C> {

};

这是Possibility to mix composite pattern and curiously recurring template pattern的简化实现。我收到以下错误:

Return type of virtual function 'foo' is not covariant with the return type of the function it overrides ('C *' is not derived from 'A *')

在clang 3.0,gcc 4.7和visual studio 2008上测试过。

第一个解决方案:

class C : public A, public B<C> {}

在视觉工作室下编辑,警告B已经是A的孩子,并且在clang下编译并出现初始错误。

另一种解决方法:

class D : public A {}
class C : public B<D> {}

解决了不完整性问题,但我无法弄清楚我将拥有多少个A实例。直觉告诉我A是虚拟的,因此应该只有一个。

此解决方法也会创建无法读取的代码。

该标准对此情况有何规定?这段代码应该编译吗?如果没有,为什么?

1 个答案:

答案 0 :(得分:4)

您的虚拟函数A::foo()会返回A*,而函数B<C>::foo()会覆盖它,会返回C*

这在理论上确实尊重协方差原理,因为C确实是(派生自)A的特化,但在实例化时,这是未知的,因为{{1是一种不完整的类型。

重新考虑您的设计的一种可能方法是将C设为类模板,并让AB的模板参数传播到T

A

关于您的解决方法:

  

该标准对此情况有何规定?这段代码应该编译吗?如果没有,为什么?

它不应该编译,因为仅template<typename T> class A { virtual T* foo() = 0; }; template<class T> class B : public A<T> { virtual T* foo() { return nullptr; } }; 的事实也明确地来自C(注意,你最终会得到两个不同的基础子AA类型的对象在实例化C时不会使C成为完整类型。根据C ++ 11标准的第9.2 / 2段:

  

类说明符的结束B<C>,类被视为完全定义的对象类型(3.9)(或完整类型)。   在类 member-specification 中,该类在函数体内被视为完整,   默认参数,以及非静态数据成员的 brace-or-equal-initializers (包括   嵌套类)。否则,它在其自己的类 member-specification 中被视为不完整。