CRTP和动态多态性编译错误

CRTP and dynamic polymorphism compile error

本文关键字:编译 错误 多态性 动态 CRTP      更新时间:2023-10-16
class A {
    virtual A* foo() = 0;
};
template<class T>
class B : public A {
    virtual T* foo() { return nullptr; }
};
class C : public B<C> {
};

这是一个简化的实现可能性混合复合模式和奇怪的重复出现的模板模式。我得到以下错误:

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

在visual studio下编译时会提示B已经是a的子节点,并且不是在clang下编译时会出现初始错误。

另一个解决方案:

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

解决了不完整性问题,但我不知道我将有多少个A实例。直觉告诉我A是虚的,因此应该只有一个。

这个方法也会产生不可读的代码。

对于这种情况,标准是怎么说的?这段代码应该编译吗?如果不是,为什么?

您的虚拟函数A::foo()返回一个A*,而函数B<C>::foo(),这意味着覆盖它,返回一个C*

这在理论上确实尊重协方差原则,因为C确实是A的专门化(派生),但在实例化时,这是未知的,因为C是一个不完全类型。

重新考虑设计的一种可能方法是使A也成为类模板,并让BT的模板参数传播到A:

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

关于你的解决方法:

对于这种情况,标准是怎么说的?这段代码应该编译吗?如果不是,为什么?

不应该编译,因为仅仅使C也显式地派生自A这一事实(注意,在C中最终会有两个不同的A类型的基本子对象)并不能使C在实例化B<C>时成为一个完整的类型。c++ 11标准第9.2/2段:

类说明符的结尾}处,类被认为是完全定义的对象类型(3.9)(或完整类型)。在类成员规范中,类在函数体中被视为完整的;默认参数,以及非静态数据成员的大括号或相等初始化式(包括嵌套类)。否则,在其自己的类成员规范中,它被认为是不完整的。