Pow*_*mer 14 c++ language-lawyer c++20
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() {}
Run Code Online (Sandbox Code Playgroud)
有什么问题a4吗?我的意思a4.x是应该由一个常量1(与它工作的地方相同a2)和a4.y一个常量初始化0,因为它a4是一个全局静态变量(与它工作的地方相同a1)。
现在,考虑到 MSVC 和 GCC 都无法编译此代码,我认为该标准以某种方式禁止它。但我以任何方式看待a4它的初始值都是精确的、恒定的并且在编译时已知,那么为什么标准禁止我们将其声明为constinit)?
除此之外,clang++还拒绝第一次constinitialization:
error: variable does not have a constant initializer
constinit A1 a1; // x == 0, y == 0.
Run Code Online (Sandbox Code Playgroud)
我认为在这种情况下我们可能会遗漏一些措辞。
首先,初始化分为三个阶段([basic.start.static]):
(1) 和 (2) 一起是静态初始化,(3) 是……嗯,动态的。所做的constinit是确保没有动态初始化,来自[dcl.constinit]:
如果用说明符声明的变量
constinit具有动态初始化([basic.start.dynamic]),则程序格式错误。
常量初始化的规则是,来自[expr.const]:
变量或临时对象在以下情况下
o被常量初始化:
- 它要么有一个初始化器,要么它的默认初始化导致执行一些初始化,并且
- 当解释为常量表达式时,其初始化的完整表达式是常量表达式,除了 if
o是一个对象,该完整表达式还可以为其o及其子对象调用 constexpr 构造函数,即使这些对象是非文字类类型。
话虽如此,让我们来看看这三种类型。从最简单的开始:
struct A2 { int x = 1; int y = 2; };
constinit A2 a2; // x == 1, y == 2.
Run Code Online (Sandbox Code Playgroud)
在这里,我们正在初始化所有成员,这显然是常量初始化。这里其实没什么问题。
下一个:
struct A1 { int x; int y; };
constinit A1 a1; // x == 0, y == 0.
Run Code Online (Sandbox Code Playgroud)
在这里,我们的默认构造函数不执行任何操作,没有初始化。所以这不是常量初始化。但是发生了零初始化,然后就没有进一步的初始化了。这里当然不需要进行动态初始化,因此无需constinit进行诊断。
最后:
struct A3 { int x = 1; int y; };
constinit A3 a4; // Error: illegal initialization of 'constinit' entity with a non-constant expression
Run Code Online (Sandbox Code Playgroud)
在采用P1331之前,这种初始化a4显然不是常量初始化,因为常量初始化明确需要完全初始化的变量。但我们不再有这个要求了,规则后来被移到了a4.y实际被阅读的时间。
但我认为这种情况下的意图很大程度上a4不是常量初始化,因为它部分未初始化。这可能是一个核心问题。
鉴于它不是常量初始化,那么我们进入零初始化。但在那之后,a4仍然没有完全初始化 - 因为我们必须设置x为1. 没有提供半常数半零初始化。该规则是恒定的,或者,如果不是恒定的,则为零。由于这不是(或者至少不应该是)常量初始化,并且零初始化是不够的,因此a4必须进行动态初始化。因此,constinit应该标记这种情况。gcc 和 msvc 在这里是正确的(clang 错误地拒绝a1)。
| 归档时间: |
|
| 查看次数: |
474 次 |
| 最近记录: |