出于某种原因,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;
}发布于 2017-06-14 15:35:36
事情是这样的。虽然在我们看来,编译器知道它所需要知道的关于所讨论的类型的一切,但是标准却不这么说。
[tem.arg.type/2]
模板类型参数可能是不完全类型的。- end注意事项
[basic.type/5]
已声明但未定义的类,特定上下文中的枚举类型(dcl.enum),或未知界限或不完全元素类型的数组,是未完全定义的对象类型.46不完全定义的对象类型,cv是不完全定义的 (basic.fundamental)类型。对象不应定义为不完整类型。
[第2级]
在看到类名之后,就会将类名插入到声明类名的作用域中。类名也被插入到类本身的范围中;这称为注入类名。为了进行访问检查,注入的类名被视为公共成员名。类说明符通常称为类定义.类被认为是在类说明符的结束大括号被看到后被定义的,尽管它的成员函数一般还没有定义。可选属性-说明符-seq属于类;属性-说明符-seq中的属性在命名时被视为类的属性。
粗体的文本描绘出编译器将类型参数T视为不完全对象类型的简单图片。就好像你只转发了声明,就像这样:
class derived;他们不能推断这个前向声明是从base派生的类。因此,他们不能接受它作为foo_default_impl上下文中的协变返回类型。@marcinj 在评论中指出
[class.virtual.虚拟/8]
如果D::f的协变量返回类型中的类类型与B::f中的类类型不同,则D::f返回类型中的类类型应在D::f的声明点完成,或为类D类型。
由于T既不是完整的,也不是foo_default_impl<T>本身,所以它不能是一个协变量返回类型。
https://stackoverflow.com/questions/44548242
复制相似问题