出于某种原因,GCC和clang的最新版本在此特定方案中无法识别返回类型协方差。错误消息具有误导性:
error: return type of virtual function 'foo' is not covariant with the return
type of the function it overrides ('derived *' is not derived from 'base *')
以下是代码:
class base
{
private:
virtual base * foo() = 0;
};
template< class T >
class foo_default_impl : public virtual base
{
private:
T * foo() override { return nullptr; }
};
class derived : public virtual base, private foo_default_impl< derived >
{
};
int main() {
derived d{}; // error: return type of virtual function 'foo' is not covariant with the return type of the function it overrides ('derived *' is not derived from 'base *')
return 0;
}
答案 0 :(得分:20)
这就是事情。对我们来说,似乎编译器知道有关所讨论类型需要知道的一切,标准另有说法。
... [注意:模板类型参数可能是不完整的类型。 - 结束说明]
已声明但未定义的类,其中包含枚举类型 某些上下文([dcl.enum]),或者一个未知界限或数组 不完整的元素类型,是一个未完全定义的对象类型 .46 未完全定义的对象类型和cv void是不完整的类型 ([basic.fundamental])。对象不应被定义为具有 不完整的类型。
将类名插入到声明它的作用域中 看到类名后立即。类名也是 插入到类本身的范围内;这被称为 注入的类名。出于访问检查的目的, inject-class-name被视为公共成员名称。一个 class-specifier通常称为类定义。 一堂课 被认为是在其类说明符的右括号之后定义的 已经看到,尽管它的成员函数一般还没有 定义。可选的attribute-specifier-seq属于该类; 之后属性说明符-seq中的属性 在命名时考虑类的属性。
粗体文字描绘了有问题的编译器将类型参数T视为不完整的对象类型的简单画面。好像你只是向前宣布它,就像这样:
class derived;
他们无法推断出此前向声明是从base
派生的类。所以他们不能在foo_default_impl
的背景下接受它作为共变体返回类型。就像 @marcinj in the comments指出的那样:
如果D :: f的协变返回类型中的类类型不同 B :: f的类型,D :: f的返回类型中的类类型应为 在D :: f的声明点完成或者是该类 输入D.
由于T
既不完整,也不是foo_default_impl<T>
本身,因此它不能是共变量返回类型。