出于某种原因,最新版本的 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;
}
最佳答案
事情就是这样。虽然在我们看来,编译器似乎知道它需要知道的关于所讨论类型的一切,但标准却另有规定。
... [ Note: A template type argument may be an incomplete type. — end note ]
A class that has been declared but not defined, an enumeration type in certain contexts ([dcl.enum]), or an array of unknown bound or of incomplete element type, is an incompletely-defined object type.46 Incompletely-defined object types and cv void are incomplete types ([basic.fundamental]). Objects shall not be defined to have an incomplete type.
A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name. A class-specifier is commonly referred to as a class definition. A class is considered defined after the closing brace of its class-specifier has been seen even though its member functions are in general not yet defined. The optional attribute-specifier-seq appertains to the class; the attributes in the attribute-specifier-seq are thereafter considered attributes of the class whenever it is named.
粗体文本描绘了所讨论的编译器将类型参数 T 视为不完整对象类型的简单图景。就好像你只转发声明它,像这样:
class derived;
他们不能推断出这个前向声明是从 base
派生的类。 .所以他们不能接受它作为 foo_default_impl
上下文中的协变返回类型。 . @marcinj in the comments 指出了类似的情况:
If the class type in the covariant return type of D::f differs from that of B::f, the class type in the return type of D::f shall be complete at the point of declaration of D::f or shall be the class type D.
自从 T
既不完整,也不是foo_default_impl<T>
本身,它不能是协变返回类型。
关于c++ - 协变返回类型无法识别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44548242/