无法识别协变返回类型

时间:2017-06-14 14:56:10

标签: c++

出于某种原因,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;
}

1 个答案:

答案 0 :(得分:20)

这就是事情。对我们来说,似乎编译器知道有关所讨论类型需要知道的一切,标准另有说法。

[temp.arg.type/2]

  

... [注意:模板类型参数可能是不完整的类型。 - 结束说明]

[basic.types/5]

  

已声明但未定义的类,其中包含枚举类型   某些上下文([dcl.enum]),或者一个未知界限或数组   不完整的元素类型,是一个未完全定义的对象类型 .46   未完全定义的对象类型和cv void是不完整的类型   ([basic.fundamental])。对象不应被定义为具有   不完整的类型。

[class/2]

  

将类名插入到声明它的作用域中   看到类名后立即。类名也是   插入到类本身的范围内;这被称为   注入的类名。出于访问检查的目的,   inject-class-name被视为公共成员名称。一个   class-specifier通常称为类定义。 一堂课   被认为是在其类说明符的右括号之后定义的   已经看到,尽管它的成员函数一般还没有   定义。可选的attribute-specifier-seq属于该类;   之后属性说明符-seq中的属性   在命名时考虑类的属性。

粗体文字描绘了有问题的编译器将类型参数T视为不完整的对象类型的简单画面。好像你只是向前宣布它,就像这样:

class derived;

他们无法推断出此前向声明是从base派生的类。所以他们不能在foo_default_impl的背景下接受它作为共变体返回类型。就像 @marcinj in the comments指出的那样:

[class.virtual/8]

  

如果D :: f的协变返回类型中的类类型不同   B :: f的类型,D :: f的返回类型中的类类型应为   在D :: f的声明点完成或者是该类   输入D.

由于T既不完整,也不是foo_default_impl<T>本身,因此它不能是共变量返回类型。

相关问题