constexpr спецификатор (начиная с C++11)
-
constexpr— указывает, что значение переменной, структурированная привязка(начиная с C++26) или функции может использоваться в константных выражениях
Пояснение
Спецификатор constexpr объявляет, что значение сущностей можно вычислить во время компиляции. Такие сущности затем можно использовать там, где допускаются только константные выражения, вычисляемые во время компиляции (при условии, что переданы подходящие аргументы функции).
Спецификатор constexpr, используемый в объявлении объекта или нестатической функции-члена(до C++14), подразумевает const.
Спецификатор constexpr, используемый в первом объявлении функции или статического члена-данных(начиная с C++17), подразумевает inline. Если какое-либо объявление функции или шаблона функции содержит спецификатор constexpr, то каждое объявление должно содержать этот спецификатор.
Переменная constexpr
Переменную или шаблон переменной(начиная с C++14) можно объявить как constexpr, если выполнены все следующие условия:
- Объявление является определением.
- Она имеет литеральный тип.
- Она инициализирована (в объявлении).
| (до C++26) |
| (начиная с C++26) |
Если переменная constexpr не является локальной для единицы трансляции, ее не следует инициализировать ссылкой на сущность, локальную для единицы трансляции и используемую в константных выражениях, а также не следует иметь подобъект, ссылающийся на такую сущность. Такая инициализация запрещена в модуле интерфейса модуля (за пределами его закрытого фрагмента модуля, если он есть) или разделе модуля и считается устаревшей в любом другом контексте. | (начиная с C++20) |
Функция constexpr
Функцию или шаблон функции можно объявить как constexpr.
Функция является подходящей для constexpr, если выполнены все следующие условия:
| (до C++20) |
| (до C++23) |
| (до C++26) |
| (начиная с C++20) |
| (до C++14) | ||
|
(начиная с C++14) (до C++23) |
За исключением инстанцированных функций constexpr, нетемплейтные функции constexpr должны быть подходящими для constexpr.
| Если для нетемплейтной функции constexpr, не являющейся конструктором и не заданной по умолчанию, не существует значений аргументов, при которых вызов функции мог бы быть вычисляемым подвыражением базового константного выражения, программа некорректна, диагностика не требуется. Если для шаблонной функции constexpr ни одна специализация шаблона функции/класса не сделает эту шаблонную функцию подходящей для constexpr при рассмотрении ее как нетемплейтной функции, программа некорректна, диагностика не требуется. | (до C++23) |
Вызов функции constexpr в заданном контексте во всех отношениях дает тот же результат, что и вызов эквивалентной функции, не являющейся constexpr, в том же контексте, за следующими исключениями:
- Вызов функции constexpr может использоваться в константном выражении.
- Устранение копирования не выполняется в константном выражении.
Конструктор constexpr
| Помимо требований к функциям constexpr, конструктор должен удовлетворять всем следующим условиям, чтобы быть подходящим для constexpr:
| (до C++26) | ||||
| Конструкторам не нужно удовлетворять каким-либо дополнительным условиям, чтобы быть подходящими для constexpr. | (начиная с C++26) |
| Если для конструктора constexpr, не заданного по умолчанию и не являющегося шаблонным, не существует значений аргументов, при которых вызов функции мог бы быть вычисляемым подвыражением полного выражения инициализации какого-либо объекта, подпадающего под действие константного выражения, программа некорректна, диагностика не требуется. | (до C++23) |
Деструктор constexpr
| Деструкторы не могут быть constexpr, однако тривиальный деструктор может неявно вызываться в константных выражениях. | (до C++20) | ||
| Помимо требований к функциям constexpr, деструктор должен удовлетворять всем следующим условиям, чтобы быть подходящим для constexpr:
|
(начиная с C++20) (до C++26) |
||
| Деструкторам не нужно удовлетворять каким-либо дополнительным условиям, чтобы быть подходящими для constexpr. | (начиная с C++26) |
Примечания
| Поскольку оператор 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
}
| (начиная с C++23) |
Конструкторы constexpr допустимы для классов, не являющихся литеральными типами. Например, конструктор по умолчанию для std::shared_ptr является constexpr, что позволяет выполнять константную инициализацию.
Переменные-ссылки можно объявлять как constexpr (их инициализаторы должны быть константными выражениями для ссылок):
static constexpr int const& x = 42; // constexpr reference to a const int object
// (the object has static storage duration
// due to life extension by a static reference)
| Хотя блоки try и встроенный ассемблер разрешены в функциях constexpr, выбрасывание исключений , которые не перехватываются(начиная с C++26) или выполнение ассемблерного кода по-прежнему запрещено в константном выражении. Если переменная имеет константное разрушение, для вызова ее деструктора не требуется генерировать машинный код, даже если деструктор не является тривиальным. Функция constexpr, которая не является лямбда-функцией, специальной функцией-членом или шаблоном, не может неявно стать немедленной функцией. Чтобы определение такой функции было корректным, пользователям необходимо явно пометить ее как consteval. | (начиная с C++20) |
| Макрос проверки поддержки возможности | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_constexpr | 200704L | (C++11) | constexpr |
201304L | (C++14) | Смягчение ограничений constexpr, методы constexpr, не являющиеся const | |
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 |
__cpp_constexpr_virtual_inheritance | 202506L | (C++26) | Виртуальное наследование constexpr |
Ключевые слова
Пример
Определяет функции constexpr C++11/14 для вычисления факториалов; определяет литеральный тип, расширяющий строковые литералы:
#include <iostream>
#include <stdexcept>
// C++11 constexpr functions use recursion rather than iteration
constexpr int factorial(int n)
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
// C++14 constexpr functions may use local variables and loops
#if __cplusplus >= 201402L
constexpr int factorial_cxx14(int n)
{
int res = 1;
while (n > 1)
res *= n--;
return res;
}
#endif // C++14
// A literal class
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 functions signal errors by throwing exceptions
// in C++11, they must do so from the conditional operator ?:
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 functions had to put everything in a single return statement
// (C++14 does not have that requirement)
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);
}
// An output function that requires a compile-time constant, for testing
template<int n>
struct constN
{
constN() { std::cout << n << '\n'; }
};
int main()
{
std::cout << "4! = ";
constN<factorial(4)> out1; // computed at compile time
volatile int k = 8; // disallow optimization using volatile
std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time
std::cout << "The number of lowercase letters in \"Hello, world!\" is ";
constN<countlower("Hello, world!")> out2; // implicitly converted to 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) in C++17,
// std::ssize(a) in C++20
std::cout << "Array of length " << length_a << " has elements: ";
for (int i = 0; i < length_a; ++i)
std::cout << a[i] << ' ';
std::cout << '\n';
}
Вывод:
4! = 24 8! = 40320 The number of lowercase letters in "Hello, world!" is 9 Array of length 12 has elements: 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.
См. также
| константное выражение | определяет выражение, которое можно вычислить во время компиляции |
consteval спецификатор(C++20) | указывает, что функция является немедленной функцией, то есть каждый вызов функции должен выполняться при константном вычислении |
constinit спецификатор(C++20) | утверждает, что переменная имеет статическую инициализацию, то есть инициализацию нулем и константную инициализацию |
Документация по C для constexpr |
|
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/constexpr