U u;
不开始的生命周期i
子对象。开始除数组类型之外的变量的生命周期char
, unsigned char
or std::byte
也不是特别有资格的操作之一隐式创建对象. [基本.介绍.对象]/13 https://timsong-cpp.github.io/cppwp/n4868/basic#intro.object-13
因此此时i
成员绝对不活动并且数组对象不活动。
正如@Sebastian 在问题评论中提到的,调用std::construct_at
on u.i
那么在常量表达式中不允许使用,因为[expr.const]/6.1 https://timsong-cpp.github.io/cppwp/n4868/expr.const#6.1特别要求提供的指针指向一个对象,该对象的生命周期开始于常量表达式的求值期间(或者是从std::allocator
).
因此,Clang 对我来说似乎是正确的。有一个针对此问题的开放 GCC 错误here https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102286.
但我不确定这是否是预期的解释,因为如果成员使用非数组类型,Clang 确实接受该程序,根据我的推理,这同样是不允许的。
相关措辞是以下结果的结果this https://github.com/cplusplus/nbballot/issues/43评论。
无论如何,隐式对象创建并不打算发生在常量表达式中,尽管目前看来是这样的(question https://stackoverflow.com/questions/70556755/does-implicit-object-creation-apply-in-constant-expressions), see CWG 问题 2469 https://wg21.cmeerw.net/cwg/issue2469.
如果没有如下所述的隐式对象创建,则在需要常量表达式的上下文中的使用应该是格式错误的,独立于std::construct_at
限制和以下考虑因素。
如果在常量表达式上下文之外使用,该构造是否具有定义的行为,我并不完全确定。
但我认为std::construct_at
被指定为相当于新表达意味着它将调用operator new
,指定在它返回的存储中隐式创建对象。[基本.介绍.对象]/13 https://timsong-cpp.github.io/cppwp/n4868/basic#intro.object-13
Whether operator new
必须是一个分配operator new
我并不完全清楚是否要求这是真的。我认为“在返回的存储区域中”的措辞不需要它。
i
属于类型int[1]
,这是一个隐式生命周期类型,如果需要,可以通过有资格隐式创建对象的操作隐式创建它们。[基本.类型.一般]/9 https://timsong-cpp.github.io/cppwp/n4868/basic#types.general-9
因此我认为construct_at
将隐式创建一个数组对象u.i
并开始它的生命周期。我也认为[基本.介绍.对象]/2 https://timsong-cpp.github.io/cppwp/n4868/basic#intro.object-2将保证该对象成为联合的子对象,以便u.i
会参考它。
然而,考虑到操作的存储只有单个的大小int
并假设这也是中的存储[基本.介绍.对象]/13 https://timsong-cpp.github.io/cppwp/n4868/basic#intro.object-13,只有长度的数组1
可以在其中隐式创建。因此如果i
长度大于1
,隐式创建的数组不能与成员完全重叠,因此不能成为联合的子对象。
在这种情况下,隐式对象创建无法进行return u.i[0];
定义的行为。
有一个关于这个问题的讨论here https://lists.isocpp.org/std-proposals/2021/09/3171.php这似乎表明已经形成了指向第一个元素的指针u.i
在其生命周期之外是 UB,在这种情况下construct_at
带数组的版本会更直接地具有 UB,但至少编译器接受两者auto x = u.i;
and auto x = &u.i[0];
不断地表达,没有抱怨。正如该答案的评论中提到的,这似乎也是错误的。
总而言之我认为std::construct_at
一般可以not用于激活数组成员union
.
但是,假设您更换std::construct_at
打电话给
u.i[0] = 1;
然后这个赋值将开始数组对象的生命周期,如中所述[class.union.general]/6 https://timsong-cpp.github.io/cppwp/n4868/class.union#general-6。从 C++20 开始,这对于常量表达式也没有被取消资格。因此,如果在需要常量表达式的上下文中使用,代码不会格式错误,也不会在该上下文之外具有未定义的行为。