在其自己的定义中使用不完整的类型是否需要诊断?



这是对这个问题的语言律师的跟进,所有答案都同意不允许以下内容:

struct A { A a; };

在类型定义本身中,有几种情况可以使用不完整的类型,例如

struct A { A *a; };
struct A { A &a; };
struct A { std::unique_ptr<A> a; };
struct A { std::vector<A> a; };     // since c++17

等。

我的问题是,编译器是否需要以不允许的方式诊断使用不完整类型的程序?即它的格式不正确?还是这些程序有 UB?还是别的什么?

我的感觉是,某些情况只是 UB,比如vector的例子,编译器通常不会诊断,即使它只在 c++17 中合法化。

为了澄清,我只是询问在类型本身的定义中使用不完整的类型,如所示示例所示。

这取决于确切的规则。必须诊断违反标准核心语言部分中与不完整类类型相关的大多数规则的情况。除非另有指定,否则用作标准库模板的模板参数是未定义的行为,这为实现提供了更大的自由度。

这些规则需要诊断(因为它们用"应"表示):

[basic.def]/5:

在对象的定义中,该对象的类型不应是不完整的类型([basic.types]),抽象类类型或其(可能是多维的)数组。

[dcl.fct.def.general]/2:

参数的类型或函数定义的返回类型不应是函数体内不完整或抽象的(可能是 cv 限定的)类类型,除非删除函数 ([dcl.fct.def.delete])。

[expr.ref]/4(关于.运算符之前的操作数表达式的类型):

类型应该是完整的,除非类成员访问出现在该类的定义中。 [注意如果类不完整,则需要在完整类类型中查找以引用相同的声明([basic.scope.class])。

由于->运算符的内置含义是用等价于(*A).BA->B定义的,这也适用于->运算符表达式。

[class.mem]/15:

非静态数据成员的类型不应是不完整的类型([basic.types])、抽象类类型([class.abstract])或其(可能是多维的)数组。[注意特别是,类C不能包含类C的非静态成员,但它可以包含指向类C对象的指针或引用。

[class.derived]/2(关于类定义的基类列表):

class-or-decltype应表示不是未完全定义的类 ([class.mem]) 的(可能符合 cv 条件的)类类型;任何 cv 限定符都将被忽略。

有更多核心语言规则禁止不完整的类类型,但以上是最常见的相关规则。另请参阅[basic.def.odr]/12中需要完整类的非规范上下文列表。

我没有看到一个直接的规则,即如果scope命名不完整的类类型,限定idscope::name格式不正确,但在这种情况下,名称查找肯定会失败,这是一个可诊断的违规。

对于标准库,禁止不完整类型作为模板参数的总体禁止是 [res.on.functions]/(2.5):

在某些情况下(替换函数、处理程序函数、对用于实例化标准库模板组件的类型的操作),C++标准库依赖于C++程序提供的组件。如果这些组件不满足其要求,则本文档对实现没有要求。

具体而言,在以下情况下未定义效果:

  • 如果在实例化模板组件或计算概念时将不完整的类型 ([basic.types]) 用作模板参数,除非该组件特别允许。

如前所述,C++17 添加了实例化类std::vector<T, Alloc>的特定权限,但如果T不完整且Alloc满足"分配器完整性要求"([vector.overview]/4),则没有其成员。

最新更新