Constant expressions
Определяет выражение , которое может быть вычислено во время компиляции.
Такие выражения могут использоваться в качестве константных аргументов шаблонов, размеров массивов и в других контекстах, требующих константных выражений, например.
int n = 1; std::array<int, n> a1; // Ошибка: "n" не является константным выражением const int cn = 2; std::array<int, cn> a2; // OK: "cn" является константным выражением
Определение
|
Выражение, принадлежащее к любой из перечисленных ниже категорий константных выражений, является константным выражением .
|
(до C++11) | ||
|
Следующие выражения в совокупности называются константными выражениями :
|
(since C++11)
(until C++14) |
||
|
Следующие сущности являются допустимыми результатами константного выражения :
Константное выражение — это либо glvalue core constant expression , которое ссылается на сущность, являющуюся допустимым результатом константного выражения, либо prvalue core constant expression, значение которого удовлетворяет следующим ограничениям:
|
(since C++14)
(until C++26) |
||
|
Константное выражение — это либо glvalue основное константное выражение , которое ссылается на объект или не- непосредственную функцию , либо prvalue основное константное выражение, значение которого удовлетворяет следующим ограничениям:
|
(начиная с C++26) |
При определении того, является ли выражение константным выражением, copy elision предполагается не выполняемым.
Определение константных выражений в C++98 полностью находится в сворачиваемом блоке. Следующее описание применяется к C++11 и более поздним версиям C++.
Литеральный тип
Следующие типы в совокупности называются literal types :
- возможно cv-квалифицированный void
- скалярный тип
- ссылочный тип
- массив литерального типа
- возможно cv-квалифицированный классовый тип, удовлетворяющий всем следующим условиям:
-
- Он имеет тривиальный деструктор (до C++20) constexpr деструктор (начиная с C++20) .
- Все его нестатические невариантные члены данных и базовые классы являются не-volatile литеральными типами.
- Он является одним из следующих типов:
| (since C++17) |
-
-
- агрегатный тип объединения , удовлетворяющий одному из следующих условий:
-
- Не имеет вариантного члена .
- Имеет хотя бы один вариантный член не-volatile литерального типа.
- не-объединенный агрегатный тип, и каждый из его анонимных членов объединения удовлетворяет одному из следующих условий:
-
- Не имеет вариантного члена.
- Имеет хотя бы один вариантный член не-volatile литерального типа.
- тип с хотя бы одним constexpr конструктором (шаблоном), который не является конструктором копирования или перемещения
-
Только объекты литеральных типов могут быть созданы в константном выражении.
Основное константное выражение
Основное константное выражение — это любое выражение, вычисление которого не должно включать вычисление ни одной из следующих языковых конструкций:
| Конструкция языка | Версия | Документ(ы) |
|---|---|---|
указатель
this
, за исключением случаев, когда он используется в
constexpr
функции
, которая вычисляется как часть выражения, или когда появляется в неявном или явном выражении доступа к члену класса
|
N2235 | |
| поток управления, проходящий через объявление блочной переменной со статической или поточной продолжительностью хранения , которая не является пригодной для использования в константных выражениях | (начиная с C++23) | P2242R3 |
|
Этот раздел не завершён
Причина: Перенести содержимое из HTML-списка ниже в вики-таблицу выше, добавив документы/вопросы CWG, которые ввели соответствующий пункт в стандарт. Мини-примеры не сохраняются, их можно объединить в один большой пример в нижней части этой страницы. |
-
a function call expression that calls a function (or a constructor) that is not declared
constexpr
constexpr int n = std::numeric_limits<int>::max(); // OK: max() является constexpr constexpr int m = std::time(nullptr); // Ошибка: std::time() не является constexpr
- a function call to a constexpr function which is declared, but not defined
- a function call to a constexpr function/constructor template instantiation where the instantiation fails to satisfy constexpr функция/конструктор requirements.
- a function call to a constexpr virtual function, invoked on an object whose dynamic type is constexpr-unknown
- an expression that would exceed the implementation-defined limits
-
an expression whose evaluation leads to any form of core language
undefined
или ошибочно
(since C++26)
behavior, except for any potential undefined behavior introduced by
стандартные атрибуты
.
constexpr double d1 = 2.0 / 1.0; // OK constexpr double d2 = 2.0 / 0.0; // Ошибка: не определено constexpr int n = std::numeric_limits<int>::max() + 1; // Ошибка: переполнение int x, y, z[30]; constexpr auto e1 = &y - &x; // Ошибка: не определено constexpr auto e2 = &z[20] - &z[3]; // OK constexpr std::bitset<2> a; constexpr bool b = a[2]; // UB, но не определено, будет ли обнаружено
- (до C++17) a лямбда-выражение
-
an lvalue-to-rvalue
неявное преобразование
unless applied to...
- glvalue типа (возможно cv-квалифицированного) std::nullptr_t
-
не-volatile glvalue литерального типа, который обозначает объект,
пригодный для использования в константных выражениях
int main() { const std::size_t tabsize = 50; int tab[tabsize]; // OK: tabsize является константным выражением // потому что tabsize пригоден для использования в константных выражениях // поскольку имеет const-квалифицированный целочисленный тип, и // его инициализатор является константным инициализатором std::size_t n = 50; const std::size_t sz = n; int tab2[sz]; // Ошибка: sz не является константным выражением // потому что sz не пригоден для использования в константных выражениях // поскольку его инициализатор не был константным инициализатором }
- не-volatile glvalue литерального типа, который ссылается на не-volatile объект, время жизни которого началось в процессе вычисления данного выражения
T*
если только указатель не содержит нулевое значение указателя или не указывает на объект, тип которого
подобен
типу
T
(начиная с C++26)
dynamic_cast
операнд которого является glvalue, ссылающейся на объект, чей динамический тип является constexpr-неизвестным
(since C++20)
reinterpret_cast
constexpr int incr(int& n) { return ++n; } constexpr int g(int k) { constexpr int x = incr(k); // Ошибка: incr(k) не является ядерным константным // выражением, потому что время жизни k // началось вне выражения incr(k) return x; } constexpr int h(int k) { int x = incr(k); // OK: x не требуется инициализировать // ядерным константным выражением return x; } constexpr int y = h(1); // OK: инициализирует y значением 2 // h(1) является ядерным константным выражением, потому что // время жизни k начинается внутри выражения h(1)
typeid
expression applied to a glvalue of polymorphic type
и этот glvalue ссылается на объект, динамический тип которого является constexpr-неизвестным
(since C++20)
|
(since C++20) |
|
(since C++26) |
constexpr void check(int i) { if (i < 0) throw i; { constexpr bool is_ok(int i) { try { check(i); } catch (...) { return false; } return true; { constexpr bool always_throw() { throw 12; return true; { static_assert(is_ok(5)); // OK static_assert(!is_ok(-1)); // OK начиная с C++26 static_assert(always_throw()); // Ошибка: неперехваченное исключение
goto
dynamic_cast
или
typeid
или выражение
new
expression
(начиная с C++26)
которое может сгенерировать исключение
когда определение типа исключения недоступно
(начиная с C++26)
void g() { const int n = 0; constexpr int j = *&n; // OK: вне лямбда-выражения [=] { constexpr int i = n; // OK: 'n' не odr-используется и не захватывается здесь constexpr int j = *&n; // Некорректно: '&n' было бы odr-использованием 'n' }; }
|
обратите внимание, что если odr-использование происходит в вызове функции замыкания, оно не ссылается на this или на охватывающую переменную, поскольку обращается к члену данных замыкания // OK: 'v' и 'm' odr-используются, но не встречаются в константном выражении // внутри вложенной лямбды auto monad = [](auto v){ return [=]{ return v; }; }; auto bind = [](auto m){ return [=](auto fvm){ return fvm(m()); }; }; // Допустимо иметь захваты автоматических объектов, созданных во время вычисления константного выражения static_assert(bind(monad(2))(monad)() == monad(2)()); |
(начиная с C++17) |
Дополнительные требования
Даже если выражение E не вычисляет ничего из перечисленного выше, определяется реализацией, является ли E core constant expression, если вычисление E привело бы к неопределённому поведению во время выполнения .
Даже если выражение E не вычисляет ничего из перечисленного выше, не определено, является ли E core constant expression, если вычисление E потребовало бы вычисления любого из следующего:
- Операция с неопределённым поведением в стандартной библиотеке .
- Вызов макроса va_start .
Для целей определения, является ли выражение ядерным константным выражением, вычисление тела функции-члена
std::
allocator
<
T
>
игнорируется, если
T
является литеральным типом.
Для целей определения, является ли выражение ядерным константным выражением, вычисление вызова тривиального копирующего/перемещающего конструктора или копирующего/перемещающего оператора присваивания union считается копирующим/перемещающим активный член union, если таковой имеется.
|
Для целей определения, является ли выражение ядерным константным выражением, вычисление выражения-идентификатора, которое именует structured binding bd , имеет следующую семантику:
|
(начиная с C++26) |
При оценке выражения как основного константного выражения все выражения идентификаторов и использования * this , которые ссылаются на объект или ссылку, время жизни которых началось вне оценки выражения, рассматриваются как ссылающиеся на конкретный экземпляр этого объекта или ссылки, время жизни которого и всех подобъектов (включая все члены объединений) охватывает всю константную оценку.
- Для такого объекта который не может использоваться в константных выражениях (начиная с C++20) , динамический тип объекта является constexpr-unknown .
- Для такой ссылки которая не может использоваться в константных выражениях (начиная с C++20) , ссылка рассматривается как привязанная к неопределенному объекту ссылочного типа, время жизни которого и всех подобъектов включает всю константную оценку и чей динамический тип является constexpr-unknown.
Целочисленное константное выражение
Целочисленное константное выражение - это выражение целочисленного типа или типа неограниченного перечисления, неявно преобразованное в prvalue, где преобразованное выражение является ядерным константным выражением.
Если выражение классового типа используется там, где ожидается целочисленное константное выражение, выражение контекстно неявно преобразуется в целочисленный тип или тип неограниченного перечисления.
Преобразованное константное выражение
Преобразованное константное выражение
типа
T
- это выражение,
неявно преобразованное
к типу
T
, где преобразованное выражение является константным выражением, и последовательность неявного преобразования содержит только:
-
- constexpr пользовательские преобразования
- преобразования lvalue-to-rvalue
- целочисленные повышения
- non-narrowing целочисленные преобразования
- повышения плавающей точки
- non-narrowing преобразования плавающей точки
| (начиная с C++17) |
И если происходит любое reference binding , оно может быть только direct binding .
Следующие контексты требуют конвертируемого константного выражения:
- константное выражение constant-expression в case labels
- инициализаторы перечислений когда базовый тип фиксирован
- целочисленные и перечислимые (until C++17) константные template arguments
|
(начиная с C++14) |
| (начиная с C++26) |
Контекстуально преобразованное константное выражение типа bool — это выражение, контекстуально преобразуемое в bool , где преобразуемое выражение является константным выражением, а последовательность преобразования содержит только указанные выше преобразования.
Следующие контексты требуют контекстно преобразованного константного выражения типа bool :
| (до C++23) | |
|
(начиная с C++17)
(до C++23) |
|
| (начиная с C++20) |
Составные сущностиСоставные значения объекта obj определяются следующим образом:
Составные ссылки объекта obj включают следующие ссылки:
Составные значения и составные ссылки переменной var определяются следующим образом:
Для любой составной ссылки ref переменной var , если ref связана с временным объектом или его подобъектом, время жизни которого продлено до времени жизни ref , то составные значения и ссылки этого временного объекта также являются составными значениями и ссылками var , рекурсивно. Сущности, представимые в constexprОбъекты со статической продолжительностью хранения являются constexpr-ссылаемыми в любой точке программы.
Объект
obj
с автоматической продолжительностью хранения является
constexpr-ссылаемым
из точки
Объект или ссылка
x
является
constexpr-представимым
в точке
|
(начиная с C++26) | ||||||||
Сущности с константной инициализацией
Применимо в константных выраженияхПеременная является потенциально-константной если она является constexpr переменной или имеет ссылочный тип, либо не-volatile const-квалифицированный целочисленный тип или тип перечисления.
Константно-инициализированная потенциально-константная переменная
var
является
используемой в константных выражениях
в точке
Явно константно вычисляемые выраженияСледующие выражения (включая преобразования в целевой тип) являются явно константно-вычисляемыми :
Можно определить, происходит ли вычисление в явно константном контексте, с помощью
std::is_constant_evaluated
и
|
(начиная с C++20) |
Функции и переменные, необходимые для константной оценки
Следующие выражения или преобразования являются потенциально константно вычисляемыми :
- явно константно-вычисляемые выражения
- потенциально вычисляемые выражения
- непосредственные подвыражения списка инициализации в фигурных скобках (константное вычисление может потребоваться для определения, является ли преобразование сужающим )
- выражения взятия адреса, встречающиеся внутри шаблонной сущности (константное вычисление может потребоваться для определения, является ли такое выражение зависимым по значению )
- подвыражения одного из вышеперечисленных, не являющиеся подвыражением вложенного невычисляемого операнда
Функция требуется для константного вычисления если она является constexpr функцией и вызывается выражением, которое потенциально вычисляется константно.
Переменная требуется для константной оценки если она является либо constexpr переменной, либо имеет не-volatile const-квалифицированный целочисленный тип или тип ссылки, и выражение-идентификатор которое её обозначает потенциально константно оценивается.
Определение функции по умолчанию и инстанцирование специализации шаблона функции или специализации шаблона переменной (since C++14) запускаются, если функция или переменная (since C++14) требуется для константного вычисления.
Постоянное подвыражение
Постоянное подвыражение — это выражение, вычисление которого в качестве подвыражения выражения e не препятствовало бы тому, чтобы e являлось ядерным константным выражением , при условии что e не является ни одним из следующих выражений:
| (since C++20) |
Примечания
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_constexpr_in_decltype
|
201711L
|
(C++20)
(DR11) |
Генерация определений функций и переменных, когда необходима для константного вычисления |
__cpp_constexpr_dynamic_alloc
|
201907L
|
(C++20) | Операции для динамической продолжительности хранения в constexpr функциях |
__cpp_constexpr
|
202306L
|
(C++26) | constexpr приведение из void * : в сторону constexpr стирания типа |
202406L
|
(C++26) | constexpr размещающий new и new [ ] | |
__cpp_constexpr_exceptions
|
202411L
|
(C++26) | constexpr исключения: [1] , [2] |
Пример
|
Этот раздел не завершён
Причина: отсутствует пример |
Отчеты о дефектах
Следующие отчеты об изменениях в поведении, являющиеся дефектными, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение как опубликовано | Корректное поведение |
|---|---|---|---|
| CWG 94 | C++98 |
арифметические константные выражения не могли
включать переменные и статические члены данных |
могут |
| CWG 366 | C++98 |
выражения, содержащие строковые литералы
могли быть целочисленными константными выражениями |
они не являются |
| CWG 457 | C++98 |
выражения с участием volatile переменных
могли быть целочисленными константными выражениями |
они не являются |
| CWG 1293 | C++11 |
было неясно, можно ли использовать строковые литералы
в константных выражениях |
их можно использовать |
| CWG 1311 | C++11 | volatile glvalues могли использоваться в константных выражениях | запрещено |
| CWG 1312 | C++11 |
reinterpret_cast
запрещён в константных выражениях,
но приведение к и из void * может достичь того же эффекта |
запрещённые преобразования
из типа cv void * в тип указателя на объект |
| CWG 1313 | C++11 |
неопределённое поведение было разрешено;
все операции вычитания указателей были запрещены |
UB запрещено; вычитание указателей
на один массив разрешено |
| CWG 1405 | C++11 |
для объектов, которые могут использоваться в константных выражениях,
их изменяемые подобъекты также могли использоваться |
они не могут использоваться |
| CWG 1454 | C++11 |
передача констант через constexpr
функции посредством ссылок не разрешалась |
разрешено |
| CWG 1455 | C++11 | преобразованные константные выражения могли быть только prvalues | могут быть lvalues |
| CWG 1456 | C++11 |
адресное константное выражение не могло
обозначать адрес за последним элементом массива |
разрешено |
| CWG 1535 | C++11 |
выражение
typeid
с операндом полиморфного
типа класса не являлось core constant выражением, даже если проверка времени выполнения не требуется |
ограничение на операнд
применяется только к glvalue полиморфных типов классов |
| CWG 1581 | C++11 |
функции, необходимые для константной оценки,
не требовалось определять или инстанцировать |
требуется |
| CWG 1613 | C++11 |
константные выражения могли вычислять любую
odr-используемую ссылку внутри лямбда-выражений |
некоторые ссылки не могли
быть вычислены |
| CWG 1694 | C++11 |
привязка значения временного объекта к ссылке со статической
продолжительностью хранения была константным выражением |
это не является
константным выражением |
| CWG 1872 | C++11 |
константные выражения могли вызывать
constexpr
инстанциации шаблонов функций
которые не удовлетворяют требованиям constexpr функций |
такие инстанциации
не могут быть вызваны |
| CWG 1952 | C++11 |
неопределённое поведение стандартной библиотеки
требовало диагностики |
не указано, требуется ли
их диагностика |
| CWG 2022 | C++98 |
определение константного выражения может
зависеть от того, выполняется ли копирование |
предполагать, что копирование
всегда выполняется |
| CWG 2126 | C++11 |
константно инициализированные временные объекты с продлённым временем жизни для const-
квалифицированных литеральных типов не могли использоваться в константных выражениях |
пригодны для использования |
| CWG 2129 | C++11 | целочисленные литералы не являлись константными выражениями | являются |
| CWG 2167 | C++11 |
ссылки не-члены, локальные для вычисления
делали вычисление не-constexpr |
ссылки не-члены
разрешены |
| CWG 2278 | C++98 | решение CWG issue 2022 не было реализуемым |
предполагать, что копирующее исключение
никогда не выполняется |
| CWG 2299 | C++14 |
было неясно, можно ли использовать макросы в
<cstdarg>
при константной оценке |
va_arg
запрещён,
va_start
не определён
|
| CWG 2400 | C++11 |
вызов constexpr виртуальной функции для объекта, не используемого
в константных выражениях и время жизни которого началось вне выражения, содержащего вызов, может быть константным выражением |
это не является
константным выражением |
| CWG 2490 | C++20 |
вызовы (псевдо)деструкторов не имели
ограничений при константной оценке |
ограничение добавлено |
| CWG 2552 | C++23 |
при вычислении константного выражения ядра управляющий
поток не мог проходить через объявление неблочной переменной |
может |
| CWG 2558 | C++11 | неопределённое значение может быть константным выражением | не является константным выражением |
| CWG 2647 | C++20 | переменные типов с квалификатором volatile могут быть потенциально-константными | они не являются таковыми |
| CWG 2763 | C++11 |
нарушение
[[
noreturn
]]
не требовалось
обнаруживать во время константной оценки |
требуется |
| CWG 2851 | C++11 |
преобразованные константные выражения не
допускали преобразований с плавающей точкой |
разрешить непредусматривающие сужение
преобразования с плавающей точкой |
| CWG 2907 | C++11 |
константные выражения не могли применять
преобразования lvalue-to-rvalue к std::nullptr_t glvalues |
могут применять такие
преобразования |
| CWG 2909 | C++20 |
переменная без инициализатора может быть
константно-инициализирована только если её инициализация по умолчанию приводит к выполнению некоторой инициализации |
может быть константно-
инициализирована только если её тип является константно-инициализируемым по умолчанию |
| CWG 2924 |
C++11
C++23 |
было не определено, является ли выражение, нарушающее
ограничения
[[
noreturn
]]
(C++11) или
[[
assume
]]
(C++23), ядерным константным выражением
|
является
определяемым реализацией |
| P2280R4 | C++11 |
вычисление выражения, содержащего идентификатор выражения
или * this который ссылается на объект или ссылку, время жизни которого началось вне этого вычисления, не является константным выражением |
может быть
константным выражением |
Смотрите также
constexpr
спецификатор
(C++11)
|
указывает, что значение переменной или функции может быть вычислено во время компиляции |
|
(C++11)
(устарело в C++17)
(удалено в C++20)
|
проверяет, является ли тип литеральным типом
(шаблон класса) |
|
Документация C
для
Константные выражения
|
|