C++20 中的 constinit 是什么?

What is `constinit` in C++20?

constinit 是一个新的 keyword and specifier in C++20 which was proposed in P1143.

标准中提供了以下示例:

const char * g() { return "dynamic initialization"; }
constexpr const char * f(bool p) { return p ? "constant initializer" : g(); }
constinit const char * c = f(true);     // OK
constinit const char * d = f(false);    // ill-formed

想到几个问题:

此问题旨在作为即将出现的有关 constinit 的一般问题的参考。

  • What does constinit mean? Why was it introduced? In which cases should we use it?

static storage duration 初始化变量可能会导致两种结果¹:

  1. 变量在编译时初始化(constant-initialization);

  2. 变量在控件第一次通过它的声明时被初始化。

情况 (2) 存在问题,因为它可能导致 static initialization order fiasco,这是与全局对象相关的危险错误的来源。

constinit关键字只能应用于具有静态存储持续时间的变量。如果修饰变量没有在编译时初始化,则程序格式错误(即无法编译)。

使用constinit确保变量在编译时初始化,并且静态初始化顺序惨败不会发生。


  • Does it make a variable immutable? Does it imply const or constexpr?

没有也没有。

但是,constexpr 确实意味着 constinit


  • Can a variable be both const and constinit? What about constexpr and constinit?

可以是const也可以是constinit。它不能既是 constexpr 又是 constinit。来自写法:

At most one of the constexpr, consteval, and constinit keywords shall appear in a decl-specifier-seq.

constexpr 不等同于 const constinit,因为前者要求 持续破坏 ,而后者则不然。


  • To which variables can the specifier be applied? Why cannot we apply it to non-static, non-thread_local variables?

它只能应用于具有静态或线程存储持续时间的变量。将它应用于其他变量没有意义,因为 constinit 是关于静态初始化的。


  • Does it have any performance advantages?

没有。但是,在编译时初始化变量的附带好处是它不需要在程序执行期间进行初始化的指令。 constinit 帮助开发人员确保情况确实如此,而无需猜测或检查生成的程序集。


¹:参见 https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables

主要问题

只有当控件通过其声明或定义时,才认为对象已初始化;否则(即控件跳转到声明或定义此对象的源文件中定义的函数,它根本看不到它)对该未初始化对象的任何访问都是未定义的行为。

此外,定义为多个翻译单元的静态持续时间对象的初始化顺序也是未定义的。您没有办法在代码中请求编译器在另一个对象之前或之后初始化一个静态对象,因为一个对象依赖于另一个对象。实际上你不能这样做。由编译器决定首先初始化哪个对象;特别是这实际上取决于编译每个源文件的顺序。

示例 - 分段错误

// main.cpp
#include "src1.h"
A a{ 10 };      // declaring an object of class A with static duration.
int main() {}

// src1.cpp
#include "src1.h"
#include "src2.h"
B b{ 20 };      // declaring an object of class B with static duration.
A::A(int x): m_x(x) { b.f(); }

//src2.cpp
#include "src2.h"
int B::f() { return m_x; }
B::B(int x):  m_x(x) { }

//src1.h
struct A { 
    private: int m_x;
    public: A(int); 
};

//src2.h
struct B { 
    private: int m_x;
    public: B(int); int f(); 
};

g++ main.cpp src1.cpp src2.cpp // OK: main.cpp should be compiled first
g++ main.cpp src2.cpp src1.cpp // OK: main.cpp should be compiled first
g++ any_other_order // sigfault

解决方法:

constinit 在 C++20 中引入