constinit 说明符 (C++20 起)
来自cppreference.com
解释
constinit 说明符声明拥有静态或线程存储期的变量。
| (C++26 起) |
如果变量以 constinit 声明,那么它的初始化声明必须应用 constinit。如果以 constinit 声明的变量拥有动态初始化(即使作为静态初始化执行),那么程序非良构。
如果在初始化声明点没有可达的 constinit 声明,那么程序非良构,不需要诊断。
constinit 不能和 constexpr 一同使用。当声明的变量为引用时,constinit 等价于 constexpr。当声明的变量为对象时,constexpr 强制对象必须拥有静态初始化和常量析构,并使对象有 const 限定,然而 constinit 不强制常量析构和 const 限定。结果是拥有 constexpr 构造函数但没有 constexpr 析构函数的类型(例如 std::shared_ptr<T>)的对象可能可以用 constinit,但不能用 constexpr 声明。
const char* g() { return "动态初始化"; }
constexpr const char* f(bool p) { return p ? "常量初始化器" : g(); }
constinit const char* c = f(true); // OK
// constinit const char* d = f(false); // 错误
constinit 也能用于非初始化声明,以告知编译器 thread_local 变量已被初始化,以减少隐藏的防卫变量所致的开销。
extern thread_local constinit int x;
int f() { return x; } // 无需检查防卫变量
注解
| 功能特性测试宏 | 值 | 标准 | 功能特性 |
|---|---|---|---|
__cpp_constinit |
201907L |
(C++20) | constinit
|
关键词
示例
运行此代码
#include <cassert>
constexpr int square(int i)
{
return i * i;
}
int twice(int i)
{
return i + i;
}
constinit int sq = square(2); // OK:编译时完成初始化
// constinit int x_x = twice(2); // 错误:需要编译时初始化器
int square_4_gen()
{
static constinit int pow = square(4);
// constinit int prev = pow; // 错误:constinit 只能应用于具有静态或线程存储期的变量
int prev = pow;
pow = pow * pow;
return prev;
}
int main()
{
assert(sq == 4);
sq = twice(1); // 与 constexpr 不同,可以随后在运行时改变它的值
assert(sq == 2);
assert(square_4_gen() == 16);
assert(square_4_gen() == 256);
assert(square_4_gen() == 65536);
}
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
| 缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
|---|---|---|---|
| CWG 2543 | C++20 | 声明为 constinit 的变量以静态初始化的一部分被动态初始化时的行为不明确
|
此时程序非良构 |
参阅
consteval 说明符 (C++20)
|
指定函数为立即函数,即对该函数的每次调用必须在常量求值中进行 |
constexpr 说明符 (C++11)
|
指定变量或函数的值能在编译时计算 |
| 常量表达式 | 定义可以在编译时求值的表达式 |
| 常量初始化 | 将静态变量的初始值设为编译期常量 |
| 零初始化 | 将对象的初始值设为零 |