Спецификатор constexpr (начиная с C++11)
constexpr- указывает, что значение переменной или функции может появляться в константных выражениях
Объяснение
Спецификатор constexpr объявляет, что можно оценить значение функции или переменной во время компиляции. Затем такие переменные и функции можно использовать там, где разрешены только константные выражения времени компиляции (при условии, что заданы соответствующие аргументы функции).
Спецификатор constexpr, используемый в объявлении объекта или нестатической функции-элементе (до C++14), подразумевает const. Спецификатор constexpr, используемый в функции или статическом элементе данных (начиная с C++17) подразумевает inline. Если какое-либо объявление функции или шаблона функции имеет спецификатор constexpr, то каждое объявление должно содержать этот спецификатор.
Переменная constexpr
Переменная constexpr должна соответствовать следующим требованиям:
- её тип должен быть LiteralType.
- она должна быть немедленно инициализирована
- полное выражение его инициализации, включая все неявные преобразования, вызовы конструкторов и т.д., должно быть константным выражением
|
|
(начиная с C++20) |
|
Если переменная |
(начиная с C++20) |
Функцияconstexpr
Функция constexpr должна соответствовать следующим требованиям:
|
(до C++20) |
|
(начиная с C++20) |
- для конструктора и деструктора (начиная с C++20), класс не должен иметь виртуальных базовых классов
|
(до C++23) |
|
(до C++14) | ||
|
(начиная с C++14) (до C++23) |
|
Конструктор
|
|
(до C++20) |
- каждый конструктор, выбранный для инициализации нестатических элементов данных и базового класса, должен быть конструктором constexpr.
Деструктор constexpr
|
Деструкторы не могут быть |
(до C++20) |
|
Деструктор
|
(начиная с C++20) |
Для constexpr шаблонов функций и constexpr функций-элементов шаблонов классов по крайней мере одна специализация должна соответствовать вышеупомянутым требованиям. Другие специализации по-прежнему считаются constexpr, хотя вызов такой функции не может появляться в константном выражении. Если никакая специализация шаблона не соответствует требованиям к функции constexpr, если рассматривать её как функцию, не являющуюся шаблоном, то шаблон неправильно сформирован, и диагностика не требуется. (до C++23)
Примечание
|
Поскольку оператор 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
// (объект имеет статическую длительность хранения
// из-за продления жизни по статической ссылке)
|
Несмотря на то, что блоки Если переменная имеет константное уничтожение, нет необходимости генерировать машинный код, чтобы вызвать для неё деструктор, даже если её деструктор не является тривиальным. |
(начиная с 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 ограничений
| |
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
|
Ключевые слова
Пример
Определяет constexpr функции C++11/14, вычисляющие факториалы; определяет тип литерала, который расширяет строковые литералы:
#include <iostream>
#include <stdexcept>
// Функции constexpr С++11 используют рекурсию, а не итерацию
constexpr int factorial(int n)
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
// Функции constexpr С++14 могут использовать локальные переменные и циклы
#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; }
};
// Функции 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
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] << " ";
}
Вывод:
4! = 24
8! = 40320
количество строчных букв в \"Привет, мир!\" равно 8
массив длинной 12 содержит элементы: 0 1 2 3 4 5 6 7 8 0 0 0
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам 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)
|
утверждает, что переменная имеет статическую инициализацию, то есть инициализация нулём и константная инициализация |
Документация C по constexpr
| |