【问题标题】:Covariant return type is not recognized协变返回类型无法识别
【发布时间】:2017-06-14 14:56:10
【问题描述】:

出于某种原因,最新版本的 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;
}

【问题讨论】:

  • foo() 需要返回 foo_default_impl *,而不是 T *
  • 以前的版本编译过你的代码吗?我想知道这是否是因为 derived 在传递给 foo_default_impl 时还不是一个完整的类型:eel.is/c++draft/class.derived#class.virtual-8
  • @KhouriGiordano:为什么?在我们正在考虑的特定情况下,T 将是derived,而derived 公开派生自base
  • @marcinj :我认为你应该把它写下来作为答案。问题是foo_default_impl::foo 违反了规则(返回类型既不完整,也不是foo_default_impl)。错误信息可能会更好。
  • @marcin :它不完整。完整类型的其中一件事是您知道它的大小。在知道基类的大小之前,您无法知道类的大小。我们目前正在尝试创建其中一个基类!

标签: c++


【解决方案1】:

事情就是这样。虽然在我们看来,编译器似乎知道它需要知道的关于所讨论类型的一切,但标准却另有规定。

[temp.arg.type/2]

... [ 注意:模板类型参数可能是不完整的类型。 — 尾注 ]

[basic.types/5]

已声明但未定义的类,枚举类型 某些上下文([dcl.enum]),或未知边界的数组或 不完整的元素类型,是不完整定义的对象类型.46 未完全定义的对象类型和 cv void 是不完全类型 ([基本.基础])。对象不应被定义为具有 类型不完整。

[class/2]

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

粗体文本描绘了所讨论的编译器将类型参数 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&lt;T&gt; 本身,所以它不能是协变返回类型。

【讨论】:

  • 我同意。部分错误消息 ('derived *' is not derived from 'base *') 让我认为编译器对这段代码感到困惑。
  • 上述 cmets 中提出的观点——sizeof(complete_type) 必须有效——也清楚地说明了为什么 derived 不能在该行完成。我们定义了derived的基数,我们怎么知道derived有多大?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
  • 2011-09-02
  • 1970-01-01
  • 1970-01-01
  • 2017-08-26
相关资源
最近更新 更多