使用 constinit 变量初始化一个 constexpr 变量
Using constinit variable to initialize a constexpr variable
看看这个小例子:
constinit int a = 0;
constexpr int b = a;
clang 不编译它 (godbolt):
2:15: error: constexpr variable 'b' must be initialized by a constant expression
这是正确的诊断吗?
如果是,为什么标准不允许这样做?我知道,a
的值可能会在 运行 期间(甚至在动态初始化期间)发生变化,但在常量初始化时,它的值是已知的,因此它可以用于初始化 b
.
这是正确的诊断吗?
我会说是的。根据cppreference:
constinit - specifies that a variable must have static initialization,
i.e. zero initialization and constant initialization, otherwise the
program is ill-formed.
静态(常量)初始化和常量表达式是不同的概念,常量表达式可以用于常量初始化但反之则不行。 constinit
不应与 const 混淆。这意味着初始化(仅)是常量。
但是,constinit const
可以用在 constexpr
中,它们应该是相同的。
反例:
constinit int a = 0;
struct X{
X() {
a = 4;
}
};
X x{};
constexpr int b = a;
b
应该是什么?
关键是 a
可以在 b
是 seen.
之前以非常量方式更改
constexpr
毫无例外地结合了constinit
和const
。
constinit
使用编译时常量表达式强制初始化,并且在静态初始化期间,不允许动态初始化。它不会以任何方式改变变量本身。
const
禁止更改变量,但可以被 mutable
成员削弱。
两者一起使其成为编译时常量表达式。
综上所述,是的,诊断是正确的。
是的,诊断是正确的。 constexpr
变量必须用常量表达式初始化,a
不是常量表达式(它是可变变量)。
constinit
(P1143) 的目的是在初始化 不是常量的情况下强制变量声明格式错误。它不会改变变量本身的任何东西,比如它的类型或任何东西(以 constexpr
隐式 const
的方式)。愚蠢的例子:
struct T {
int i;
constexpr T(int i) : i(i) { }
T(char c) : i(c) { }
};
constinit T c(42); // ok
constinit T d('X'); // ill-formed
这就是 constinit
的全部内容,唯一真正的规则是 [dcl.constinit]/2:
If a variable declared with the constinit
specifier has dynamic initialization ([basic.start.dynamic]), the program is ill-formed.
[ Note: The constinit
specifier ensures that the variable is initialized during static initialization ([basic.start.static]).
— end note
]
constinit
中的const只指初始化,不是变量,不是任何类型。请注意,它也不会 更改 执行的初始化类型,它只是诊断是否执行了错误的类型。
在:
constinit int a = 0;
constexpr int b = a;
0
是常量表达式,所以a
的初始化是合式的。一旦我们越过那个,说明符就不会做任何事情。相当于:
int a = 0; // same behavior, a undergoes constant initialization
constexpr int b = a;
这是完全错误的。
but at constant-initialization, its value is known, so it could be used to initialize b
.
当然,现在。怎么样:
constinit int a = 0;
cin >> a;
constexpr int b = a;
那显然不会飞。允许这样做需要扩展什么是常量表达式(在我看来这已经是标准中最复杂的规则)以允许非常量变量但仅在初始化后立即出现?复杂性似乎不值得,因为你总是可以写:
constexpr int initializer = 0;
constinit int a = initializer;
constexpr int b = initializer;
看看这个小例子:
constinit int a = 0;
constexpr int b = a;
clang 不编译它 (godbolt):
2:15: error: constexpr variable 'b' must be initialized by a constant expression
这是正确的诊断吗?
如果是,为什么标准不允许这样做?我知道,a
的值可能会在 运行 期间(甚至在动态初始化期间)发生变化,但在常量初始化时,它的值是已知的,因此它可以用于初始化 b
.
这是正确的诊断吗?
我会说是的。根据cppreference:
constinit - specifies that a variable must have static initialization, i.e. zero initialization and constant initialization, otherwise the program is ill-formed.
静态(常量)初始化和常量表达式是不同的概念,常量表达式可以用于常量初始化但反之则不行。 constinit
不应与 const 混淆。这意味着初始化(仅)是常量。
但是,constinit const
可以用在 constexpr
中,它们应该是相同的。
反例:
constinit int a = 0;
struct X{
X() {
a = 4;
}
};
X x{};
constexpr int b = a;
b
应该是什么?
关键是 a
可以在 b
是 seen.
constexpr
毫无例外地结合了constinit
和const
。
constinit
使用编译时常量表达式强制初始化,并且在静态初始化期间,不允许动态初始化。它不会以任何方式改变变量本身。
const
禁止更改变量,但可以被 mutable
成员削弱。
两者一起使其成为编译时常量表达式。
综上所述,是的,诊断是正确的。
是的,诊断是正确的。 constexpr
变量必须用常量表达式初始化,a
不是常量表达式(它是可变变量)。
constinit
(P1143) 的目的是在初始化 不是常量的情况下强制变量声明格式错误。它不会改变变量本身的任何东西,比如它的类型或任何东西(以 constexpr
隐式 const
的方式)。愚蠢的例子:
struct T {
int i;
constexpr T(int i) : i(i) { }
T(char c) : i(c) { }
};
constinit T c(42); // ok
constinit T d('X'); // ill-formed
这就是 constinit
的全部内容,唯一真正的规则是 [dcl.constinit]/2:
If a variable declared with the
constinit
specifier has dynamic initialization ([basic.start.dynamic]), the program is ill-formed. [ Note: Theconstinit
specifier ensures that the variable is initialized during static initialization ([basic.start.static]). — end note ]
constinit
中的const只指初始化,不是变量,不是任何类型。请注意,它也不会 更改 执行的初始化类型,它只是诊断是否执行了错误的类型。
在:
constinit int a = 0;
constexpr int b = a;
0
是常量表达式,所以a
的初始化是合式的。一旦我们越过那个,说明符就不会做任何事情。相当于:
int a = 0; // same behavior, a undergoes constant initialization
constexpr int b = a;
这是完全错误的。
but at constant-initialization, its value is known, so it could be used to initialize
b
.
当然,现在。怎么样:
constinit int a = 0;
cin >> a;
constexpr int b = a;
那显然不会飞。允许这样做需要扩展什么是常量表达式(在我看来这已经是标准中最复杂的规则)以允许非常量变量但仅在初始化后立即出现?复杂性似乎不值得,因为你总是可以写:
constexpr int initializer = 0;
constinit int a = initializer;
constexpr int b = initializer;