constexpr
specifier
(since C++11)
-
-
constexpr- указывает, что значение переменной , structured binding (since C++26) или функции может использоваться в constant expressions
-
Содержание |
Объяснение
Спецификатор constexpr объявляет, что значение сущности может быть вычислено во время компиляции. Такие сущности могут затем использоваться там, где допускаются только константные выражения времени компиляции (при условии передачи соответствующих аргументов функции).
Спецификатор constexpr , используемый в объявлении объекта или нестатической функции-члена (до C++14) , подразумевает const .
Спецификатор constexpr , используемый в первом объявлении функции или static члена данных (начиная с C++17) , подразумевает inline . Если любое объявление функции или шаблона функции содержит спецификатор constexpr , то каждое объявление должно содержать этот спецификатор.
constexpr переменная
Переменная или шаблон переменной (начиная с C++14) может быть объявлена constexpr если выполняются все следующие условия:
- Объявление является определением .
- Оно имеет literal type .
- Оно инициализируется (объявлением).
|
(до C++26) |
|
(начиная с C++26) |
|
Если constexpr переменная не является translation-unit-local , она не должна инициализироваться ссылкой на translation-unit-local сущность, которая может использоваться в константных выражениях, а также не должна иметь подобъект, ссылающийся на такую сущность. Такая инициализация запрещена в module interface unit (вне его private module fragment , если таковой имеется) или в module partition, и устарела в любом другом контексте. |
(since C++20) |
constexpr функция
Функция или шаблон функции может быть объявлен constexpr .
Функция является constexpr-подходящей если выполняются все следующие условия:
|
(до C++20) |
|
(до C++23) |
|
(начиная с C++20) |
|
(до C++14) | ||
|
(начиная с C++14)
(до C++23) |
За исключением инстанцированных constexpr функций, нешаблонные constexpr функции должны быть constexpr-совместимыми.
|
Для неконструкторной функции constexpr , которая не является ни дефолтной, ни шаблонной, если не существует значений аргументов, при которых вызов функции мог бы быть вычисляемым подвыражением core constant expression , программа является некорректной, диагностика не требуется. Для шаблонной функции constexpr , если ни одна специализация функции/шаблона класса не сделает шаблонную функцию constexpr-подходящей при рассмотрении в качестве нешаблонной функции, программа является некорректной, диагностика не требуется. |
(до C++23) |
Вызов constexpr функции в заданном контексте дает тот же результат, что и вызов эквивалентной не- constexpr функции в том же контексте во всех отношениях, за следующими исключениями:
- Вызов constexpr функции может появляться в константном выражении .
- Копирование исключений не выполняется в константном выражении.
constexpr конструктор
В дополнение к требованиям для constexpr функций, конструктор также должен удовлетворять всем следующим условиям, чтобы быть подходящим для constexpr:
|
(до C++23) |
- Класс не имеет ни одного virtual base class .
|
Для конструктора constexpr , который не является ни дефолтным, ни шаблонным, если не существует значений аргументов, при которых вызов функции мог бы быть вычисляемым подвыражением полного выражения инициализации некоторого объекта, подлежащего константному выражению , программа является некорректной, диагностика не требуется. |
(до C++23) |
constexpr деструктор
|
Деструкторы не могут быть constexpr , но тривиальный деструктор может быть неявно вызван в константных выражениях. |
(до C++20) | ||
|
В дополнение к требованиям для constexpr функций, деструктор также должен удовлетворять всем следующим условиям, чтобы быть подходящим для constexpr:
|
(начиная с C++20) |
Примечания
|
Поскольку оператор
constexpr int f(); constexpr bool b1 = noexcept(f()); // false, undefined constexpr function constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // true, f() is a constant expression |
(до C++17) |
|
Возможно написать constexpr-функцию, вызов которой никогда не может удовлетворять требованиям ядерного константного выражения: void f(int& i) // not a constexpr function { i = 0; } constexpr void g(int& i) // well-formed since C++23 { f(i); // unconditionally calls f, cannot be a constant expression } |
(since C++23) |
Конструкторы constexpr разрешены для классов, которые не являются литеральными типами. Например, конструктор по умолчанию std::shared_ptr является constexpr, что позволяет постоянную инициализацию .
Ссылочные переменные могут быть объявлены как constexpr (их инициализаторы должны быть reference constant expressions ):
static constexpr int const& x = 42; // constexpr ссылка на const int объект // (объект имеет статическую продолжительность хранения // благодаря продлению времени жизни статической ссылкой)
|
Несмотря на то, что try блоки и встроенный ассемблер разрешены в constexpr функциях, генерация исключений которые не перехватываются (начиная с C++26) или выполнение ассемблерного кода по-прежнему запрещено в константных выражениях. Если переменная имеет константное разрушение, нет необходимости генерировать машинный код для вызова её деструктора, даже если её деструктор не является тривиальным. Нелямбда, неспециальная и нетемплейтная constexpr функция не может неявно стать немедленной функцией. Пользователи должны явно пометить её consteval чтобы сделать такое определение функции корректным. |
(начиная с C++20) |
| Макрос тестирования возможностей | Значение | Стандарт | Функция |
|---|---|---|---|
__cpp_constexpr
|
200704L
|
(C++11) | constexpr |
201304L
|
(C++14) | Ослабленный constexpr , не- const constexpr методы | |
201603L
|
(C++17) | Constexpr лямбда | |
201907L
|
(C++20) | Тривиальная инициализация по умолчанию и asm-декларация в constexpr функциях | |
202002L
|
(C++20) | Изменение активного члена union при константной оценке | |
202110L
|
(C++23) | Не- литеральные переменные, метки и goto операторы в constexpr функциях | |
202207L
|
(C++23) | Ослабление некоторых constexpr ограничений | |
202211L
|
(C++23) | Разрешение static constexpr переменных в constexpr функциях | |
202306L
|
(C++26) | Constexpr приведение из void * : в сторону constexpr стирания типов | |
__cpp_constexpr_in_decltype
|
201711L
|
(C++11)
(DR) |
Генерация определений функций и переменных, когда необходимы для константной оценки |
__cpp_constexpr_dynamic_alloc
|
201907L
|
(C++20) | Операции для динамической продолжительности хранения в constexpr функциях |
Ключевые слова
Пример
Определяет C++11/14 constexpr функции, вычисляющие факториалы; определяет литеральный тип, расширяющий строковые литералы:
#include <iostream> #include <stdexcept> // C++11 constexpr функции используют рекурсию вместо итерации constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // C++14 constexpr функции могут использовать локальные переменные и циклы #if __cplusplus >= 201402L constexpr int factorial_cxx14(int n) { int res = 1; while (n > 1) res *= n--; return res; } #endif // C++14 // Литеральный класс class conststr { const char* p; std::size_t sz; public: template<std::size_t N> constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {} // constexpr функции сигнализируют об ошибках, выбрасывая исключения // в C++11 они должны делать это через условный оператор ?: constexpr char operator[](std::size_t n) const { return n < sz ? p[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz; } }; // C++11 constexpr функции должны были помещать всё в один оператор return // (C++14 не имеет этого требования) constexpr std::size_t countlower(conststr s, std::size_t n = 0, std::size_t c = 0) { return n == s.size() ? c : 'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) : countlower(s, n + 1, c); } // Функция вывода, требующая константу времени компиляции, для тестирования template<int n> struct constN { constN() { std::cout << n << '\n'; } }; int main() { std::cout << "4! = "; constN<factorial(4)> out1; // вычисляется во время компиляции volatile int k = 8; // запретить оптимизацию с помощью volatile std::cout << k << "! = " << factorial(k) << '\n'; // вычисляется во время выполнения std::cout << "Количество строчных букв в \"Hello, world!\" равно "; constN<countlower("Hello, world!")> out2; // неявно преобразовано в conststr constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; constexpr int length_a = sizeof a / sizeof(int); // std::size(a) в C++17, // std::ssize(a) в C++20 std::cout << "Массив длиной " << length_a << " содержит элементы: "; for (int i = 0; i < length_a; ++i) std::cout << a[i] << ' '; std::cout << '\n'; }
Вывод:
4! = 24 8! = 40320 Количество строчных букв в "Hello, world!" равно 9 Массив длиной 12 содержит элементы: 0 1 2 3 4 5 6 7 8 0 0 0
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 1358 | C++11 |
шаблонные
constexpr
функции также требовали
наличия хотя бы одного допустимого значения аргумента |
не требуется |
| CWG 1359 | C++11 |
constexpr
конструкторы объединений
должны инициализировать все члены данных |
инициализирует ровно один член
данных для непустых объединений |
| CWG 1366 | C++11 |
классы с
constexpr
конструкторами, тела функций которых
имеют вид = default или = delete могли иметь виртуальные базовые классы |
такие классы не могут
иметь виртуальные базовые классы |
| CWG 1595 | C++11 |
constexpr
делегирующие конструкторы требовали
чтобы все задействованные конструкторы были constexpr |
требуется только чтобы целевой
конструктор был constexpr |
| CWG 1712 | C++14 |
шаблон переменной
constexpr
требовал, чтобы
все его объявления содержали спецификатор constexpr [1] |
больше не требуется |
| CWG 1911 | C++11 | constexpr конструкторы для нелитеральных типов не допускались | допускаются при постоянной инициализации |
| CWG 2004 | C++11 |
копирование/перемещение объединения с изменяемым членом
допускалось в константном выражении |
изменяемые варианты делают недопустимым
неявное копирование/перемещение |
| CWG 2022 | C++98 |
дают ли эквивалентные
constexpr
и не-
constexpr
функции равный результат может зависеть от того, выполняется ли пропуск копирования |
предполагается, что пропуск копирования всегда
выполняется в константных выражениях |
| CWG 2163 | C++14 |
метки допускались в
constexpr
функциях
несмотря на то, что операторы goto запрещены |
метки также запрещены |
| CWG 2268 | C++11 |
копирование/перемещение объединения с изменяемым членом было
запрещено решением CWG issue 2004 |
допускается если объект создан
внутри константного выражения |
| CWG 2278 | C++98 | решение CWG issue 2022 не было реализуемым |
предполагается, что пропуск копирования никогда
не выполняется в константных выражениях |
| CWG 2531 | C++11 |
невстроенная переменная становилась встроенной
если переобъявлялась с constexpr |
переменная не
становится встроенной |
- ↑ Это избыточно, потому что не может быть более одного объявления шаблона переменной с constexpr спецификатором.
Смотрите также
| constant expression | определяет выражение , которое может быть вычислено во время компиляции |
consteval
specifier
(C++20)
|
указывает, что функция является немедленной функцией , то есть каждый вызов функции должен быть в константном вычислении |
constinit
specifier
(C++20)
|
утверждает, что переменная имеет статическую инициализацию, т.е. zero initialization и constant initialization |
|
C documentation
для
constexpr
|
|