协变返回类型、常量和不完整类

Posted

技术标签:

【中文标题】协变返回类型、常量和不完整类【英文标题】:Covariant return types, const-ness, and incomplete classes 【发布时间】:2016-07-22 19:08:35 【问题描述】:

此代码在 g++ 6.1 下编译成功,但使用 clang 3.8 时出错:

class C;

class Base 
public:
  virtual const C *getC();
;

class Derived : public Base 
public:
  virtual       C *getC();
;

clang报错如下:

$ dev/compilers/linux-x86_64-2.12.2/clang3.8/bin/clang++ -Wall -c testcovariantreturn.cxx
testcovariantreturn.cxx:10:20: error: return type of virtual function 'getC' is not covariant with the return type of the function it overrides ('C' is incomplete)

如果类 C 是完全定义的而不是前向声明的,则没有错误。我的理解是,在覆盖虚拟方法时,协变允许“较小”的 cv 限定(即,从返回类型中删除 const)。

clang 是否正确/允许需要完整的类型,如果是,为什么?有 C 的定义可用如何改变这里的任何东西?

这并不完全是学术性的,在大型代码库中我不愿意添加不必要的包含,我们尝试将声明作为标准做法。

【问题讨论】:

此代码是协变的,因此理想情况下它应该被接受。相反的const 方差不会是协变的,并且会支持原始const 对象的UB 修改。这就是说(这很重要)我不知道 C++ 标准对此有什么看法,如果有的话。 【参考方案1】:

这是 clang 3.8 的错误,特别是 26297。来自 [class.virtual],来自 N4594 的措辞:

重写函数的返回类型应与重写函数的返回类型相同 或 协变 与函数的类。如果函数 D::f 覆盖函数 B::f,则返回类型 如果满足以下条件,则这些函数是协变的: (7.1) — 都是指向类的指针,都是指向类的左值引用,或者都是指向类的右值引用 课程 (7.2)——B::f返回类型中的类与D::f返回类型中的类是同一个类,或者是 D::f返回类型中类的明确且可访问的直接或间接基类 (7.3) — 指针或引用都具有相同的 cv 限定和 D::f 返回类型中的类类型 与B::f的返回类型中的类类型具有相同或更少的cv-qualification。

具有B::f return C const*D::f return C* 符合所有这些要求(指针都不是 cv 限定的,D::f 的类类型比基类更少 cv 限定),因此应该允许。

对完整性没有要求; C 不需要完整即可检查这些标准。

【讨论】:

哦,我应该在评论之前阅读您的回答。所以现在我也知道标准是怎么说的了。赞成。 :) 能否请您提及您引用的标准或草案,因为详细的措辞会随着时间的推移而变化。谢谢。 @Cheersandhth.-Alf 当然。这部分并没有真正改变。 2005 年草案的措辞基本相同(关于右值引用的模数措辞)。 我能找到的唯一半相关的 clang 错误是 llvm.org/bugs/show_bug.cgi?id=17859 这实际上与我的问题相反——它声称 clang 允许删除非类返回类型的 const... @ChrisMorley 这是llvm.org/bugs/show_bug.cgi?id=26297,已添加到答案中。【参考方案2】:

我也发现您的代码没有任何问题。它可以使用 clang 的 head 版本和我尝试过的所有编译器进行编译,除了 clang 3.8 和更早版本。

Live demo

相关标准文本:

10.3 注释 8:

如果 D::f 的协变返回类型中的类类型不同于 B::f 的返回类型中的类类型应为 完成在 D::f 的声明点 or 应该是类 D型。

协变方法的类类型必须相同或 完整,但据我了解,仍会考虑 const/volatile 差异 相同的类类型,使您的示例合法。

【讨论】:

const 差异对协方差很重要。如果覆盖将const 添加到指针对象,则它可以在类型系统中(无强制转换)返回指向最初const 对象的指针。然后,使用基类虚函数的客户端代码代码可以尝试修改原来的 const 对象,= UB。 我不确定这里是否支持 C++ 标准,但无论标准的措辞如何,它都非常重要。 :) @Cheersandhth.-Alf,这很重要,但我想特别提到的是当 incomplete 类型被允许时的措辞。 我在上面发布 cmets 时不知道,但Barry's answer 引用了其中一个 C++ 标准,因此现在很明显这个答案是错误的(就其含义而言) . 谢谢,演示工具不错

以上是关于协变返回类型、常量和不完整类的主要内容,如果未能解决你的问题,请参考以下文章

java-协变返回类型

返回专用模板类的协变类型

java遗珠之协变返回类型

CLR 协变逆变

覆盖虚函数协变返回类型(两个指针)

Covariant Returen Types(协变返回类型)