关于c ++:CRTP和动态多态编译错误

CRTP and dynamic polymorphism compile error

1
2
3
4
5
6
7
8
9
10
11
12
class A {
    virtual A* foo() = 0;
};

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

class C : public B<C> {

};

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

1
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上测试。

第一解决方案:

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

在Visual Studio下编译,警告B已经是A的子级,并且不会在出现初始错误的clang下编译。

另一个解决方法:

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

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

此外,此解决方案还会创建不可读的代码。

关于这种情况,标准规定了什么?这段代码应该编译吗?如果不是,为什么?


虚拟函数A::foo()返回A*,而函数B::foo()则返回C*,该函数旨在重写它。

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

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

1
2
3
4
5
6
7
8
9
template<typename T>
class A {
    virtual T* foo() = 0;
};

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

关于您的解决方案:

What does the standard states about this situation? Should this code compile? If not, why?

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

A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier.
Within the class member-specification, the class is regarded as complete within function bodies,
default arguments, and brace-or-equal-initializers for non-static data members (including such things in
nested classes). Otherwise it is regarded as incomplete within its own class member-specification.