Спецификатор constexpr (с C++11)
-
constexpr- указывает, что значение переменной или функции может использоваться в константных выражениях
Объяснение
Спецификатор constexpr объявляет, что значение функции или переменной может быть вычислено на этапе компиляции. Такие переменные и функции могут быть использованы там, где разрешены только константные выражения времени компиляции (при условии, что предоставлены соответствующие аргументы функции).
Спецификатор constexpr, используемый в объявлении объекта или нестатического члена-функции(до C++14), подразумевает const. Спецификатор constexpr, используемый в объявлении функции или статического члена данных(с C++17), подразумевает inline. Если какое-либо объявление функции или шаблона функции имеет спецификатор constexpr, то каждое объявление должно содержать этот спецификатор.
Переменная constexpr
Переменная constexpr должна удовлетворять следующим требованиям:
- её тип должен быть LiteralType
- она должна быть немедленно инициализирована
- полное выражение её инициализации, включая все неявные преобразования, вызовы конструкторов и т.д., должно быть константным выражением
Если переменная constexpr не является локальной для единицы трансляции, она не должна быть инициализирована указанием на, ссылкой на или иметь (возможно рекурсивный) подобъект, который указывает на или ссылается на сущность, локальную для единицы трансляции, используемую в константных выражениях. Такая инициализация запрещена в модульном интерфейсном блоке (вне его частной фрагмента модуля, если таковой имеется) или в модульной секции и устарела в любом другом контексте. | (с C++20) |
Функция constexpr
Функция constexpr должна удовлетворять следующим требованиям:
| (до C++20) |
| (с C++20) |
- для конструктора и деструктора(с C++20) класс не должен иметь виртуальных базовых классов
| (до C++23) |
| (до C++14) | ||
|
(с C++14) (до C++23) |
Конструктор constexprКонструктор constexpr, тело которого не
Деструктор constexpr
| (до C++23) |
Для шаблонов функций constexpr и constexpr-членов-функций шаблонов классов по крайней мере одна специализация должна удовлетворять вышеуказанным требованиям. Другие специализации всё ещё считаются constexpr, даже если вызов такой функции не может появиться в константном выражении. Если ни одна специализация шаблона не удовлетворяла бы требованиям к constexpr-функции, если рассматривать её как нешаблонную функцию, шаблон некорректен; диагностика не требуется.(до C++23)
Примечания
| Поскольку оператор 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::unique_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++20) |
| Макросы проверки функций | Значение | Стандарт | Функция |
|---|---|---|---|
__cpp_constexpr | 200704L | (C++11) | constexpr |
| 201304L | (C++14) | Смягчённые constexpr, методы constexpr без const | |
| 201603L | (C++17) | Функция лямбда constexpr | |
| 201907L | (C++20) | Тривиальная инициализация по умолчанию и ассемблерные инструкции в функциях constexpr | |
| 202002L | (C++20) | Изменение активного члена объединения в константном вычислении | |
| 202110L | (C++23) | Переменные, не являющиеся литералами, метки и goto операторы в функциях constexpr |
|
| 202207L | (C++23) | Смягчение некоторых ограничений constexpr | |
| 202211L | (C++23) | Разрешение статических 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>
// 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 1712 | C++14 | шаблон переменной constexpr должен был содержать все его объявления со спецификатором constexpr (это избыточно, так как не может быть более одного объявления шаблона переменной со спецификатором constexpr) | больше не требуется |
| CWG 1911 | C++11 | конструкторы constexpr для типов, не являющихся литералами, не были разрешены | разрешены при константной инициализации |
| CWG 2004 | C++11 | копирование/перемещение объединения с изменяемым членом было разрешено в константном выражении | изменяемые члены делают неявное копирование/перемещение недопустимым |
| CWG 2163 | C++14 | метки были разрешены в функциях constexpr несмотря на запрет операторов goto | метки также запрещены |
| CWG 2268 | C++11 | копирование/перемещение объединения с изменяемым членом было запрещено решением проблемы CWG 2004 | разрешено, если объект создаётся в рамках константного выражения |
См. также
| константное выражение | определяет выражение, которое может быть вычислено во время компиляции |
спецификатор 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/w/cpp/language/constexpr