[英]Why constinit of half-initialized struct does not work
struct A1 { int x; int y; };
struct A2 { int x = 1; int y = 2; };
struct A3 { int x = 1; int y; };
constinit A1 a1; // x == 0, y == 0.
constinit A2 a2; // x == 1, y == 2.
A3 a3; // x == 1, y == 0.
constinit A3 a4; // Error: illegal initialization of 'constinit' entity with a non-constant expression
int main() {}
a4
有什么问题? 我的意思是a4.x
应该由常量1
初始化(与它工作的a2
相同)和a4.y
由常量0
初始化,因为a4
是全局 static 变量(与它工作的a1
相同)。
现在,考虑到 MSVC 和 GCC 都无法编译这段代码,我假设标准以某种方式禁止它。 但无论如何我看a4
它的初始值是准确的,常量并且在编译时已知所以为什么标准禁止我们将它声明为constinit
)?
除此之外, clang++
还拒绝了第一个constinit
ialization:
error: variable does not have a constant initializer
constinit A1 a1; // x == 0, y == 0.
我认为在这种情况下,我们可能会遗漏一些措辞。
首先,初始化分为三个阶段( [basic.start.static] ):
(1) 和 (2) 一起是 static 初始化,(3) 是……好吧,动态的。 constinit
所做的是确保没有来自[dcl.constinit]的动态初始化:
如果用
constinit
说明符声明的变量具有动态初始化 ([basic.start.dynamic]),则程序格式错误。
常量初始化规则来自[expr.const] :
变量或临时 object
o
是常量初始化的,如果
- 它要么有一个初始化器,要么它的默认初始化导致执行一些初始化,并且
- 当解释为常量表达式时,其初始化的完整表达式是一个常量表达式,除非
o
是 object,该完整表达式也可以为o
及其子对象调用 constexpr 构造函数,即使这些对象是非文字的class 种。
话虽如此,让我们通过三种类型来了解 go。 从最简单的开始:
struct A2 { int x = 1; int y = 2; };
constinit A2 a2; // x == 1, y == 2.
在这里,我们正在初始化所有成员,这显然是常量初始化。 这里不是一个真正的问题。
下一个:
struct A1 { int x; int y; };
constinit A1 a1; // x == 0, y == 0.
在这里,我们的默认构造函数没有...什么都没有,没有初始化。 所以这不是常量初始化。 但是零初始化发生了,然后就没有进一步的初始化了。 这里肯定没有必须进行的动态初始化,所以constinit
没有什么可以诊断的。
最后:
struct A3 { int x = 1; int y; };
constinit A3 a4; // Error: illegal initialization of 'constinit' entity with a non-constant expression
在采用P1331之前, a4
的这种初始化显然不是常量初始化,因为常量初始化明确需要完全初始化的变量。 但是我们不再有这个要求了,规则后来被移动了 - 到实际读取a4.y
的时候。
但我认为在这种情况下的意图是a4
不是常量初始化,因为它部分未初始化。 这可能值得一个核心问题。
鉴于不是常量初始化,那么我们把go改成零初始化。 但在那之后, a4
仍然没有完全初始化——因为我们必须将x
设置为1
。 没有提供半常数半零初始化。 规则是不变的,或者如果不是那样,则为零。 由于这不是(或者至少不应该是)常量初始化,并且零初始化是不够的,因此a4
必须进行动态初始化。 因此, constinit
应该标记这种情况。 gcc 和 msvc 在这里是正确的(clang 错误地拒绝a1
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.