Что такое 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жадный? Почему он был введен? В каких случаях мы должны его использовать?

Инициализация переменной со статической продолжительностью хранения может привести к двум результатам¹:

  1. Переменная инициализируется во время компиляции (постоянная инициализация);

  2. Переменная инициализируется при первом прохождении управления через ее объявление.

Случай (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

Другие вопросы по тегам