Спецификатор constexpr (начиная с C++11)
constexpr- указывает, что значение переменной или функции может появляться в константных выражениях
Содержание |
[править] Объяснение
Спецификатор constexpr объявляет, что можно оценить значение функции или переменной во время компиляции. Затем такие переменные и функции можно использовать там, где разрешены только константные выражения времени компиляции (при условии, что заданы соответствующие аргументы функции).
Спецификатор constexpr, используемый в объявлении объекта или нестатической функции-элементе (до C++14), подразумевает const. Спецификатор constexpr, используемый в функции или статическом элементе данных (начиная с C++17) подразумевает inline. Если какое-либо объявление функции или шаблона функции имеет спецификатор constexpr, то каждое объявление должно содержать этот спецификатор.
Переменная constexpr должна соответствовать следующим требованиям:
- её тип должен быть LiteralType.
- она должна быть немедленно инициализирована
- полное выражение его инициализации, включая все неявные преобразования, вызовы конструкторов и т.д., должно быть константным выражением
|
|
(начиная с C++20) |
|
Если переменная |
(начиная с C++20) |
Функция constexpr должна соответствовать следующим требованиям:
|
(до C++20) |
|
(начиная с C++20) |
- её возвращаемый тип (если есть) должен быть LiteralType
- для конструктора и деструктора (начиная с C++20), класс не должен иметь виртуальных базовых классов
- существует по крайней мере один набор значений аргументов, таких что вызов функции может быть оценённым подвыражением основного константного выражения (для конструкторов достаточно использовать константный инициализатор) (начиная с C++14). Для нарушения этого пункта диагностика не требуется.
|
(до C++14) | ||
|
(начиная с C++14) (до C++23) |
Конструктор constexpr, тело функции которого не является =delete;, должен соответствовать следующим дополнительным требованиям:
|
(до C++20) |
- каждый конструктор, выбранный для инициализации нестатических элементов данных и базового класса, должен быть конструктором constexpr.
|
Деструкторы не могут быть |
(до C++20) |
|
Деструктор
|
(начиная с C++20) |
Для constexpr шаблонов функций и constexpr функций-элементов шаблонов классов по крайней мере одна специализация должна соответствовать вышеупомянутым требованиям. Другие специализации по-прежнему считаются constexpr, хотя вызов такой функции не может появляться в константном выражении. Если никакая специализация шаблона не соответствует требованиям к функции constexpr, если рассматривать её как функцию, не являющуюся шаблоном, то шаблон неправильно сформирован, и диагностика не требуется. (до C++23)
[править] Примечание
|
Поскольку оператор noexcept всегда возвращает constexpr int f(); constexpr bool b1 = noexcept(f()); // false, неопределённая constexpr функция constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // true, f() является константным выражением |
(до C++17) |
Конструкторы constexpr разрешены для классов, не являющихся литеральными типами. Например, конструктором по умолчанию для std::unique_ptr является constexpr, допускающий константную инициализацию.
Ссылочные переменные могут быть объявлены constexpr (их инициализаторы должны быть ссылочными константными выражениями):
static constexpr int const& x = 42; // constexpr ссылка на объект const int // (объект имеет статическую длительность хранения // из-за продления жизни по статической ссылке)
|
Несмотря на то, что блоки try и встроенный ассемблер разрешены в функциях constexpr, создание исключений или выполнение ассемблера по-прежнему запрещено в константном выражении. Если переменная имеет константное уничтожение, нет необходимости генерировать машинный код, чтобы вызвать для неё деструктор, даже если её деструктор не является тривиальным. |
(начиная с 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) | Изменение активного элемента объединения в константной оценке | |
| 202110L | (C++23) | Не литеральные переменные, метки и операторы goto в функциях constexpr | |
| 202207L | (C++23) | Ослабление некоторых constexpr ограничений | |
__cpp_constexpr_in_decltype |
201711L | (C++11) (DR) |
Генерация определений функций и переменных, когда это необходимо для константной оценки |
__cpp_constexpr_dynamic_alloc |
201907L | (C++20) | Операции для динамической длительности хранения в функциях constexpr |
[править] Ключевые слова
[править] Пример
Определение constexpr функции C++11, которая вычисляет факториалы, и литеральный тип, расширяющий строковые литералы:
#include <iostream> #include <stdexcept> // Функции constexpr С++11 используют рекурсию, а не итерацию // (Функции constexpr С++14 могут использовать локальные переменные и циклы) constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // литеральный класс 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; } }; // Функции constexpr С++11 должны были помещать всё в один оператор return // (С++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 << "количество строчных букв в \"Привет, мир!\" равно "; constN<countlower("Привет, мир!")> out2; // неявно преобразуется в conststr }
Вывод:
4! = 24 8! = 40320 количество строчных букв в \"Привет, мир!\" равно 8
[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 1712 | C++14 | шаблон переменной constexpr требовал, чтобы все его объявления содержали спецификатор constexpr (это избыточно, поскольку не может быть более одного объявления шаблона переменной со спецификатором constexpr) |
больше не требуется |
| CWG 1911 | c++11 | конструкторы constexpr для нелитеральных типов были запрещены | разрешено в константной инициализации |
| CWG 2004 | c++11 | копирование/перемещение объединения с mutable элементом было разрешено в константном выражении |
mutable варианты исключают неявное копирование/перемещение |
| CWG 2163 | c++14 | метки были разрешены в функциях constexpr, хотя переходы запрещены |
метки также запрещены |
| CWG 2268 | c++11 | копирование/перемещение объединения с mutable элементом запрещено CWG проблема 2004 |
разрешено, если объект создаётся внутри константного выражения |
[править] Смотрите также
| константное выражение | определяет выражение, которое можно оценить во время компиляции |
спецификатор consteval (C++20)
|
указывает, что функция является немедленной функцией, то есть каждый вызов функции должен оцениваться константно |
спецификатор constinit (C++20)
|
утверждает, что переменная имеет статическую инициализацию, то есть инициализация нулём и константная инициализация |

