为什么半初始化结构的 constinit 不起作用
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
是一个全局静态变量(与它工作的 a1
相同)。
现在,考虑到 MSVC 和 GCC 都无法编译此代码,我假设标准以某种方式禁止它。但无论如何我看 a4
它的初始值是准确的,常量并且在编译时已知所以为什么标准禁止我们将它声明为 constinit
)?
除此之外,clang++
还拒绝第一个 constinit
初始化:
error: variable does not have a constant initializer
constinit A1 a1; // x == 0, y == 0.
我认为在这种情况下我们可能会遗漏一些措辞。
首先,初始化分为三个阶段 ([basic.start.static]):
- 常量初始化
- 如果不是那样,zero-initialization(对于静态存储持续时间变量,如本问题中的变量)
- 如有必要,动态初始化
(1) 和 (2) 一起是静态初始化,(3) 是……好吧,动态的。而 constinit
所做的是确保没有动态初始化,来自 [dcl.constinit]:
If a variable declared with the constinit
specifier has dynamic initialization ([basic.start.dynamic]), the program is ill-formed.
常量初始化的规则是,从[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 for o
and its subobjects even if those objects are of non-literal class types.
话虽如此,让我们来看看这三种类型。从最简单的开始:
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.
在这里,我们的默认构造函数...什么也没做,没有初始化。所以这不是常量初始化。但是 zero-initialization 发生了,然后就没有进一步的初始化了。这里肯定没有必须进行的动态初始化,因此 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
是 而不是 常量初始化,因为它被部分未初始化。这可能值得一个核心问题。
既然不是常量初始化,那我们就进入零初始化。但在那之后,a4
仍然没有完全初始化 - 因为我们必须将 x
设置为 1
。没有提供 half-constant half-zero 初始化。规则是不变的,或者如果不是那样,则为零。由于这不是(或者至少不应该是)常量初始化,并且 zero-initialization 是不够的,因此 a4
必须进行动态初始化。因此,constinit
应该标记这种情况。 gcc 和 msvc 在这里是正确的(clang 错误地拒绝 a1
)。
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
是一个全局静态变量(与它工作的 a1
相同)。
现在,考虑到 MSVC 和 GCC 都无法编译此代码,我假设标准以某种方式禁止它。但无论如何我看 a4
它的初始值是准确的,常量并且在编译时已知所以为什么标准禁止我们将它声明为 constinit
)?
除此之外,clang++
还拒绝第一个 constinit
初始化:
error: variable does not have a constant initializer
constinit A1 a1; // x == 0, y == 0.
我认为在这种情况下我们可能会遗漏一些措辞。
首先,初始化分为三个阶段 ([basic.start.static]):
- 常量初始化
- 如果不是那样,zero-initialization(对于静态存储持续时间变量,如本问题中的变量)
- 如有必要,动态初始化
(1) 和 (2) 一起是静态初始化,(3) 是……好吧,动态的。而 constinit
所做的是确保没有动态初始化,来自 [dcl.constinit]:
If a variable declared with the
constinit
specifier has dynamic initialization ([basic.start.dynamic]), the program is ill-formed.
常量初始化的规则是,从[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.
话虽如此,让我们来看看这三种类型。从最简单的开始:
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.
在这里,我们的默认构造函数...什么也没做,没有初始化。所以这不是常量初始化。但是 zero-initialization 发生了,然后就没有进一步的初始化了。这里肯定没有必须进行的动态初始化,因此 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
是 而不是 常量初始化,因为它被部分未初始化。这可能值得一个核心问题。
既然不是常量初始化,那我们就进入零初始化。但在那之后,a4
仍然没有完全初始化 - 因为我们必须将 x
设置为 1
。没有提供 half-constant half-zero 初始化。规则是不变的,或者如果不是那样,则为零。由于这不是(或者至少不应该是)常量初始化,并且 zero-initialization 是不够的,因此 a4
必须进行动态初始化。因此,constinit
应该标记这种情况。 gcc 和 msvc 在这里是正确的(clang 错误地拒绝 a1
)。