Что такое constinit в C++20?
constinit
- это новое ключевое слово и спецификатор в C++20, предложенный в 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
жадный? Почему он был введен? В каких случаях мы должны его использовать?Делает ли это неизменной переменную? Означает ли это
const
илиconstexpr
?Может ли переменная быть обоими
const
а такжеconstinit
? Как насчетconstexpr
а такжеconstinit
?К каким переменным можно применить спецификатор? Почему мы не можем применить его к не-
static
, не-thread_local
переменные?Есть ли преимущества в производительности?
Этот вопрос предназначен для использования в качестве справочника при ответе на следующие вопросы о constinit
В основном.
2 ответа
- Что значит
constinit
жадный? Почему он был введен? В каких случаях мы должны его использовать?
Инициализация переменной со статической продолжительностью хранения может привести к двум результатам¹:
Переменная инициализируется во время компиляции (постоянная инициализация);
Переменная инициализируется при первом прохождении управления через ее объявление.
Случай (2) проблематичен, потому что он может привести к фиаско статического порядка инициализации, что является источником опасных ошибок, связанных с глобальными объектами.
В constinit
ключевое слово может применяться только к переменным со статической продолжительностью хранения. Если декорированная переменная не инициализируется во время компиляции, программа имеет неправильный формат (т.е. не компилируется).
С помощью constinit
гарантирует, что переменная инициализируется во время компиляции и что фиаско с порядком статической инициализации не может произойти.
- Делает ли это неизменной переменную? Означает ли это
const
илиconstexpr
?
Нет и нет.
Однако, constexpr
подразумевает constinit
.
- Может ли переменная быть обоими
const
а такжеconstinit
? Как насчетconstexpr
а такжеconstinit
?
Это может быть как const
а также constinit
. Не может быть одновременноconstexpr
а также constinit
. Из формулировки:
Максимум один из
constexpr
,consteval
, а такжеconstinit
ключевые слова должны появиться в декларации-спецификаторе-seq.
constexpr
не эквивалентно const constinit
, поскольку первое требует постоянного разрушения, а второе - нет.
- К каким переменным можно применить спецификатор? Почему мы не можем применить его к не-
static
, не-thread_local
переменные?
Его можно применять только к переменным со статической продолжительностью хранения или продолжительностью хранения потока. Не имеет смысла применять его к другим переменным, так какconstinit
все о статической инициализации.
- Есть ли преимущества в производительности?
Нет. Однако дополнительным преимуществом инициализации переменной во время компиляции является то, что она не требует инструкций для инициализации во время выполнения программы. constinit
помогает разработчикам убедиться, что это так, без необходимости угадывать или проверять созданную сборку.
¹: См. https://en.cppreference.com/w/cpp/language/storage_duration
ГЛАВНАЯ ПРОБЛЕМА:
Объект считается инициализированным только тогда, когда элемент управления проходит через его объявление или определение; в противном случае (т.е. переход управления в функцию, определенную в исходном файле, в котором этот объект объявлен или определен, он его вообще не видит) любой доступ к этому неинициализированному объекту является поведением undefined.
Более того, порядок инициализации объектов статической продолжительности, определенных в нескольких единицах перевода, также не определен. У вас нет способа в коде запросить у компилятора инициализацию статического объекта до или после другого, потому что один объект зависит от другого. На самом деле вы не можете этого сделать. Компилятор должен решить, какой объект следует инициализировать первым; в частности, это зависит от порядка компиляции каждого исходного файла.
Пример — ошибка сегментации
// 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