【问题标题】:Can I legally reuse fields in aggregate struct initialization?我可以在聚合结构初始化中合法地重用字段吗?
【发布时间】:2021-05-28 08:40:13
【问题描述】:

下面的程序使用 gcc 编译并运行,没有警告:

#include <iostream>
struct A { int a, b; };
int main() {
  A x = {.a = 42, .b = x.a}; // <-- b is initialized from x.a
  std::cout << x.a << ' ' << x.b << std::endl;
}

42 42

x 本质上是在其初始化期间使用的。这对于 .a 由大型表达式初始化的情况非常方便。

这是 C++ 中的合法表达式吗?我能保证总是得到正确的答案吗?

【问题讨论】:

  • AFAIK 指定的初始值设定项是 C++20 中的新元素,因此您的 c++17 标记似乎不适用。
  • 我认为它会起作用,因为 当前对象的成员按其自然顺序初始化,除非使用指示符, 我认为这意味着除非指示符更改顺序.所以在你的情况下,只要.a = 42先发生,然后.b = x.a就可以了。
  • 如果是 UB,他们会禁止它....不确定我会假设,一般来说。 :)
  • @Kostas:C++ 在 C++20 之前没有指定初始化器,尽管在那之前有几个 编译器 允许它。
  • Kostas: "如果你这样做 A x = {.b = 5, .a = x.b}; 你会得到一个错误。" - 这是 C++ 中的错误,而不是 C 中的错误。(当同时存在 C和 C++ 标记)

标签: c++ initialization language-lawyer


【解决方案1】:

[dcl.init.aggr]

聚合元素的初始化按元素顺序进行评估。 也就是说,与给定元素相关的所有值计算和副作用都按顺序排列在其后面的任何元素之前。

因此,x.a 已在 x.b 初始化之前被初始化。到目前为止,一切顺利。

[基本生活]

对象或引用的生命周期是对象或引用的运行时属性。如果一个变量是默认初始化的,并且如果它是类类型或其(可能是多维的)数组,则该变量被称为具有空初始化,该类类型具有一个普通的默认构造函数。 T 类型对象的生命周期开始于:

  • 获得了适合类型 T 的对齐方式和大小的存储,并且
  • 其初始化(如果有)已完成(包括空初始化)([dcl.init]),

x.a 的生命周期已经开始,尽管 x 的生命周期尚未开始。


类似地,在对象的生命周期开始之前......任何引用原始对象的左值都可以使用,但只能以有限的方式使用。 对于正在构造或销毁的对象,请参阅 [class.cdtor]。 否则,这样的glvalue指的是分配的存储([basic.stc.dynamic.allocation]),并且使用不依赖于其值的glvalue的属性是明确定义的。 如果出现以下情况,则程序具有未定义的行为:

  • glvalue 用于访问对象,或
  • ...

“访问”已定义:

[defns.access]

⟨执行时动作⟩读取或修改对象的值

注 1:只能访问标量类型的对象。 标量对象的读取在 [conv.lval] 中描述,标量对象的修改在 [expr.ass]、[expr.post.incr] 和 [expr.pre.incr] 中描述。 尝试读取或修改类类型的对象通常会调用构造函数或赋值运算符;这种调用本身并不构成访问,尽管它们可能涉及对标量子对象的访问。 ——尾注]

根据这个注释,访问x.a 不是访问x 命名的类对象。因此,初始化x.a 就足够了,并且示例定义明确且OK。

小问题:注释不规范。


编辑:我删除了适用于“构建中/构建中”对象的规则的引号,并假设这些不适用于正在初始化的聚合。


附:也许为了清楚起见,或者甚至只是为了不让代码的读者担心合法性,考虑使用中间变量:

int temp = 42;
A x = {.a = temp, .b = temp};

P.P.S 指定的初始化程序在 C++20 中首次引入标准 C++。它们不在 C+17 中。

【讨论】:

  • [class.cdtor] 适用于构造函数(和析构函数)调用期间的期间,但在聚合初始化中没有。
  • @eerorika:这是说“施工期间”的部分。这意味着“在构造函数期间”。如果他们对所有对象都意味着它,那将是“在初始化期间”。
  • @LanguageLawyer @NicolBolas 够公平的。
  • 这有点棘手,因为泛泛值 x 用于访问 x 在 C++ 中,无法访问非标量对象类型。顺便说一句,比较 [basic.life]/(6.2) 和 /(7.2)。
  • @LanguageLawyer 再次公平。然后我觉得这个例子没有问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-23
  • 2013-12-12
  • 2021-10-12
  • 2021-06-18
  • 1970-01-01
  • 2020-10-22
  • 1970-01-01
相关资源
最近更新 更多