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
? 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).
Now, considering that both MSVC and GCC fail to compile this code I assume the standard somehow forbids it. 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
)?
In addition to this, clang++
also rejects the first 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] ):
(1) and (2) together are static initialization, (3) is... well, dynamic. And what constinit
does is ensure that there is no dynamic iniitialization, from [dcl.constinit] :
If a variable declared with the
constinit
specifier has dynamic initialization ([basic.start.dynamic]), the program is ill-formed.
And the rule for constant initialization is, from [expr.const] :
A variable or temporary object
o
is constant-initialized if
- 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.
With that said, let's go through the three types. 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.
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. But we don't have this requirement anymore, the rule was moved later - to when a4.y
would actually be read.
But I think the intent in this case is very much that a4
is not constant initialized, due to it being partially uninitialized. This probably merits a core issue.
Given that it is not constant initialization, then we go into zero initialization. But after that, a4
still isn't fully initialized - because we have to set x
to 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. And thus, constinit
should flag this case. gcc and msvc are correct here (clang erroneously rejects a1
).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.