第7.5章: constinit
概述
constinit 保证变量在编译期初始化,但与 constexpr
不同,变量本身不必是常量表达式。它只是确保初始化
发生在任何动态初始化之前。
这对避免静态初始化顺序问题极其有用。
1. 基础示例
constinit 变量必须用常量初始化器初始化,但变量本身
不必是 constexpr。
#include
constinit int counter = 0; // guaranteed compile-time initialization
int main()
{
using namespace ::fast_io::iomnp;
++counter; // OK: not constexpr
print("counter = ", counter, "\n");
}
初始化在编译期完成,但变量在运行时是可变的。
2. 为什么这很有用
constinit 解决了一个真实问题:静态初始化顺序灾难。
它确保全局变量在任何动态初始化开始之前就已完成初始化。
没有 constinit 时,不同翻译单元中的两个全局变量
可能以不可预测的顺序初始化。
3. 避免静态初始化顺序问题
考虑位于不同文件中的两个全局变量:
// file A
#include
constinit int global_value = 42;
// file B
extern int global_value;
int compute()
{
return global_value * 2; // safe: global_value is initialized early
}
由于 global_value 是 constinit 的,因此保证它在
compute() 运行之前就已完成初始化。
4. 与 constexpr 的区别
constexpr 意味着:
- 值必须是常量表达式
- 变量不能被修改
constinit 意味着:
- 变量必须在编译期初始化
- 变量在运行时可以被修改
- 值不必是常量表达式
constexpr int a = 5; // constant expression, immutable
constinit int b = 5; // compile-time init, mutable
b 可以在运行时改变;a 不能。
5. constinit 不强制常量求值
constinit 只要求初始化器是常量初始化器。它并不
强制该变量必须在常量表达式中使用。
constinit int x = 10; // must be initialized at compile time
int y = x + 5; // runtime use is fine
这使 constinit 非常适合那些必须尽早初始化、但仍然可变的
全局状态。
要点
constinit保证编译期初始化。- 变量本身在运行时可以被修改。
- 它避免了静态初始化顺序问题。
- 它与
constexpr不同,后者要求常量表达式。 - 它非常适合必须尽早初始化的全局变量。