C++20. Два новых ключевых слова: consteval и constinit

  Дмитрий Бушуев  | 

  Обновл. 2 Дек 2020  | 

 423

 ǀ   2 

Стандарт C++20 принес нам два новых ключевых слова: consteval и constinit. Ключевое слово consteval объявляет функцию, результат которой вычисляется на этапе компиляции, а ключевое слово constinit гарантирует, что переменная будет инициализирована на этапе компиляции.

Прочитав предоставленное выше описание ключевых слов consteval и constinit, у вас может сложиться впечатление, что их действия очень напоминают действие спецификатора constexpr. Поэтому, прежде чем сравнивать между собой работу спецификаторов consteval, constinit, constexpr и старого доброго const, мы пройдемся по новым спецификаторам consteval и constinit.

Спецификатор consteval

Спецификатор consteval определяет так называемую безотлагательную функцию. Каждый вызов безотлагательной функции создает константу времени компиляции. Говоря простыми словами, consteval-функция выполняется во время компиляции.

Спецификатор consteval не может быть применен к деструкторам или функциям, которые выделяют/освобождают память. В объявлении функции допускается использование не более одного спецификатора consteval, constexpr или constinit. Безотлагательная функция неявно является встроенной функцией и должна удовлетворять требованиям constexpr-функции.

В C++14 есть свои требования к constexpr-функциям и, следовательно, consteval-функциям.

constexpr-функция может:

   иметь инструкции условного перехода или инструкции цикла;

   иметь больше, чем одну инструкцию;

   вызывать constexpr-функции. В то же время consteval-функция может вызывать только constexpr-функцию, но не наоборот;

   иметь фундаментальные типы данных, которые должны быть инициализированы с помощью константного значения.

сonstexpr-функции не могут иметь статических или thread_local (локальных для потока) данных. Также они не могут иметь ни блока try, ни оператора goto.

Ниже представлен пример использования consteval-функции sqr():

Результат выполнения программы:

sqr(5): 25
sqr(a): 25

Число 5 является константным значением и может использоваться в качестве аргумента для функции sqr() (вариант №1).

То же самое справедливо и для переменной a (вариант №2). Константная переменная, такая как a, может использоваться в константном выражении, когда она инициализируется константным значением.

Переменная b (вариант №3) не является константной переменной. Следовательно, вызов функции sqr(b) не является допустимым.

Спецификатор constinit


Спецификатор constinit может быть применен к переменным, имеющим статическую продолжительность жизни или продолжительность жизни потока:

   Глобальные переменные, статические переменные или статические члены класса имеют статическую продолжительность жизни. Эти объекты создаются при запуске программы и освобождаются при её завершении.

   Переменные типа thread_local имеют продолжительность жизни потока, т.е. создаются (в начале своего использования) для каждого потока, работающего с этими данными, и принадлежат исключительно потоку.

Спецификатор constinit гарантирует, что вышеперечисленные переменные будут инициализированы во время компиляции:

Результат выполнения программы:

sqr(5): 25
sqr(5): 25
sqr(5): 25

Переменные res1 и res2 имеют статическую продолжительность жизни. Переменная res3 имеет продолжительность жизни потока.

Выполнение функций

Следующая программа имеет 3 версии функции возведения числа в квадрат:

Рассмотрим вышеприведенные функции:

   sqrRunTime() — это обычная функция, работающая во время выполнения программы;

   consteval-функция sqrCompileTime() выполняется во время компиляции;

   constexpr-функция sqrRunOrCompileTime() может работать во время компиляции или во время выполнения программы.

Запрос результата во время компиляции с помощью sqrRunTime() (ошибка №1) является ошибкой, как и использование неконстантного значения в качестве аргумента для sqrCompileTime() (ошибка №2).

Разница между constexpr-функцией sqrRunOrCompileTime() и consteval-функцией sqrCompileTime() заключается в том, что sqrRunOrCompileTime() будет выполняться во время компиляции только тогда, когда этого требует контекст:

Первые три строки (1) требуют вычислений во время компиляции. Строка (2) может быть вычислена только во время выполнения программы, поскольку переменная a не является константой. Наиболее важной для нас является строка (3) — функция может быть выполнена во время компиляции или во время выполнения программы. Решение о времени выполнения зависит от компилятора и применяемого уровня оптимизации. Это замечание не относится к строке (4) — consteval-функция всегда выполняется во время компиляции.

Инициализация переменных


В следующей программе происходит сравнение ключевых слов const, constexpr и constinit:

Результат выполнения программы:

res: 1001
++constinitVal++: 1001

Во время выполнения программы инициализируется только const-переменная (1). constexpr- и constinit-переменные инициализируются во время компиляции.

constinit (3) не подразумевает константности как const (2) или constexpr(2). Переменная, объявленная как constexpr (4) или const (1), может быть локальной, а переменная, объявленная как constinit — нет.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (7 оценок, среднее: 5,00 из 5)
Загрузка...

Комментариев: 2

  1. Аватар somebox:

    >constinit не подразумевает константности

    Интересно, а почему она тогда так называется?

    1. Дмитрий Бушуев Дмитрий Бушуев:

      В данном случае подразумевается, что переменная будет статически инициализирована постоянным значением.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *