[英]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() {}
What is the problem with a4
? a4
有什么问题? I mean a4.x
should be initialized by a constant 1
(same as for a2
where it works) and a4.y
by a constant 0
because a4
is a global static variable (same as for a1
where it works).我的意思是a4.x
应该由常量1
初始化(与它工作的a2
相同)和a4.y
由常量0
初始化,因为a4
是全局 static 变量(与它工作的a1
相同)。
Now, considering that both MSVC and GCC fail to compile this code I assume the standard somehow forbids it.现在,考虑到 MSVC 和 GCC 都无法编译这段代码,我假设标准以某种方式禁止它。 But any way I look at a4
its initial value is exact, constant and known at compile time so why the standard forbids us from declaring it as constinit
)?但无论如何我看a4
它的初始值是准确的,常量并且在编译时已知所以为什么标准禁止我们将它声明为constinit
)?
In addition to this, clang++
also rejects the first constinit
ialization:除此之外, clang++
还拒绝了第一个constinit
ialization:
error: variable does not have a constant initializer
constinit A1 a1; // x == 0, y == 0.
I think this is a case where we might be missing some wording.我认为在这种情况下,我们可能会遗漏一些措辞。
To start with, there are three stages of initialization that happen ( [basic.start.static] ):首先,初始化分为三个阶段( [basic.start.static] ):
(1) and (2) together are static initialization, (3) is... well, dynamic. (1) 和 (2) 一起是 static 初始化,(3) 是……好吧,动态的。 And what constinit
does is ensure that there is no dynamic iniitialization, from [dcl.constinit] : constinit
所做的是确保没有来自[dcl.constinit]的动态初始化:
If a variable declared with the
constinit
specifier has dynamic initialization ([basic.start.dynamic]), the program is ill-formed.如果用constinit
说明符声明的变量具有动态初始化 ([basic.start.dynamic]),则程序格式错误。
And the rule for constant initialization is, from [expr.const] :常量初始化规则来自[expr.const] :
A variable or temporary object
o
is constant-initialized if变量或临时 objecto
是常量初始化的,如果
- either it has an initializer or its default-initialization results in some initialization being performed, and它要么有一个初始化器,要么它的默认初始化导致执行一些初始化,并且
- the full-expression of its initialization is a constant expression when interpreted as a constant-expression , except that if
o
is an object, that full-expression may also invoke constexpr constructors foro
and its subobjects even if those objects are of non-literal class types.当解释为常量表达式时,其初始化的完整表达式是一个常量表达式,除非o
是 object,该完整表达式也可以为o
及其子对象调用 constexpr 构造函数,即使这些对象是非文字的class 种。
With that said, let's go through the three types.话虽如此,让我们通过三种类型来了解 go。 Starting with the easiest:从最简单的开始:
struct A2 { int x = 1; int y = 2; };
constinit A2 a2; // x == 1, y == 2.
Here, we're initializing all the members, this is clearly constant initialization.在这里,我们正在初始化所有成员,这显然是常量初始化。 Not really much of a question here.这里不是一个真正的问题。
Next:下一个:
struct A1 { int x; int y; };
constinit A1 a1; // x == 0, y == 0.
Here, our default constructor does... nothing, there is no initialization.在这里,我们的默认构造函数没有...什么都没有,没有初始化。 So this is not constant initialization.所以这不是常量初始化。 But zero-initialization happens and then there is no further initialization.但是零初始化发生了,然后就没有进一步的初始化了。 There is certainly no dynamic initialization that has to take place here, so there is nothing for constinit
to diagnose.这里肯定没有必须进行的动态初始化,所以constinit
没有什么可以诊断的。
Lastly:最后:
struct A3 { int x = 1; int y; };
constinit A3 a4; // Error: illegal initialization of 'constinit' entity with a non-constant expression
Prior to the adoption of P1331 , this initialization of a4
was clearly not constant initialization because constant initialization explicitly required fully initialized variables.在采用P1331之前, a4
的这种初始化显然不是常量初始化,因为常量初始化明确需要完全初始化的变量。 But we don't have this requirement anymore, the rule was moved later - to when a4.y
would actually be read.但是我们不再有这个要求了,规则后来被移动了 - 到实际读取a4.y
的时候。
But I think the intent in this case is very much that a4
is not constant initialized, due to it being partially uninitialized.但我认为在这种情况下的意图是a4
不是常量初始化,因为它部分未初始化。 This probably merits a core issue.这可能值得一个核心问题。
Given that it is not constant initialization, then we go into zero initialization.鉴于不是常量初始化,那么我们把go改成零初始化。 But after that, a4
still isn't fully initialized - because we have to set x
to 1
.但在那之后, a4
仍然没有完全初始化——因为我们必须将x
设置为1
。 There is no provision for half-constant half-zero initialization.没有提供半常数半零初始化。 The rule is constant or, if not that, zero.规则是不变的,或者如果不是那样,则为零。 Since this isn't (or, at least, shouldn't be) constant initialization, and zero-initialization is insufficient, a4
must undergo dynamic initialization.由于这不是(或者至少不应该是)常量初始化,并且零初始化是不够的,因此a4
必须进行动态初始化。 And thus, constinit
should flag this case.因此, constinit
应该标记这种情况。 gcc and msvc are correct here (clang erroneously rejects a1
). gcc 和 msvc 在这里是正确的(clang 错误地拒绝a1
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.