Namespaces
Variants

constexpr specifier (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Содержание

Объяснение

Спецификатор constexpr объявляет, что значение сущности может быть вычислено во время компиляции. Такие сущности могут затем использоваться там, где допускаются только константные выражения времени компиляции (при условии передачи соответствующих аргументов функции).

Спецификатор constexpr , используемый в объявлении объекта или нестатической функции-члена (до C++14) , подразумевает const .

Спецификатор constexpr , используемый в первом объявлении функции или static члена данных (начиная с C++17) , подразумевает inline . Если любое объявление функции или шаблона функции содержит спецификатор constexpr , то каждое объявление должно содержать этот спецификатор.

constexpr переменная

Переменная или шаблон переменной (начиная с C++14) может быть объявлена constexpr если выполняются все следующие условия:

(до C++26)
(начиная с C++26)

  • Имеет константное разрушение, что означает, что должно быть удовлетворено одно из следующих условий:
  • Он не является классом или (возможно, многомерным) массивом таковых.
  • Он является классом с constexpr деструктором или (возможно, многомерным) массивом таковых, и для гипотетического выражения e , единственным эффектом которого является разрушение объекта, e было бы core constant expression , если бы время жизни объекта и его неизменяемых подобъектов (но не изменяемых подобъектов) считалось начинающимся внутри e .

Если constexpr переменная не является translation-unit-local , она не должна инициализироваться ссылкой на translation-unit-local сущность, которая может использоваться в константных выражениях, а также не должна иметь подобъект, ссылающийся на такую сущность. Такая инициализация запрещена в module interface unit (вне его private module fragment , если таковой имеется) или в module partition, и устарела в любом другом контексте.

(since C++20)

constexpr функция

Функция или шаблон функции может быть объявлен constexpr .

Функция является constexpr-подходящей если выполняются все следующие условия:

(до C++20)
  • Её возвращаемый тип (если существует) является литеральным типом .
  • Каждый из типов её параметров является литеральным типом.
(до C++23)
(начиная с C++20)
  • Её тело функции представляет собой = default , = delete , или составной оператор, содержащий только следующее:
(до C++14)
  • Её тело функции представляет собой = default , = delete , или составной оператор, который (до C++20) не содержит следующее:
(до C++20)
(начиная с C++14)
(до C++23)

За исключением инстанцированных constexpr функций, нешаблонные constexpr функции должны быть constexpr-совместимыми.

Для неконструкторной функции constexpr , которая не является ни дефолтной, ни шаблонной, если не существует значений аргументов, при которых вызов функции мог бы быть вычисляемым подвыражением core constant expression , программа является некорректной, диагностика не требуется.

Для шаблонной функции constexpr , если ни одна специализация функции/шаблона класса не сделает шаблонную функцию constexpr-подходящей при рассмотрении в качестве нешаблонной функции, программа является некорректной, диагностика не требуется.

(до C++23)

Вызов constexpr функции в заданном контексте дает тот же результат, что и вызов эквивалентной не- constexpr функции в том же контексте во всех отношениях, за следующими исключениями:

constexpr конструктор

В дополнение к требованиям для constexpr функций, конструктор также должен удовлетворять всем следующим условиям, чтобы быть подходящим для constexpr:

  • Его тело функции = delete или удовлетворяет следующим дополнительным требованиям:
  • Если класс является union с вариантами-членами, инициализирован ровно один из них.
  • Если класс является union-подобным классом , но не является union, для каждого из его анонимных union-членов с вариантами-членами инициализирован ровно один из них.
  • Каждый невариантный нестатический член данных и подобъект базового класса инициализирован.
(до C++20)
  • Если конструктор является делегирующим конструктором , целевой конструктор является constexpr конструктором.
  • Если конструктор является неделегирующим конструктором, каждый конструктор, выбранный для инициализации нестатических членов данных и подобъектов базовых классов, является constexpr конструктором.
(до C++23)

Для конструктора constexpr , который не является ни дефолтным, ни шаблонным, если не существует значений аргументов, при которых вызов функции мог бы быть вычисляемым подвыражением полного выражения инициализации некоторого объекта, подлежащего константному выражению , программа является некорректной, диагностика не требуется.

(до C++23)

constexpr деструктор

Деструкторы не могут быть constexpr , но тривиальный деструктор может быть неявно вызван в константных выражениях.

(до C++20)

В дополнение к требованиям для constexpr функций, деструктор также должен удовлетворять всем следующим условиям, чтобы быть подходящим для constexpr:

  • Для каждого подобъекта классового типа или (возможно многомерного) массива таких объектов, этот классовый тип должен иметь constexpr деструктор.
(до C++23)
  • Класс не имеет никаких виртуальных базовых классов.
(начиная с C++20)

Примечания

Поскольку оператор noexcept всегда возвращает true для константного выражения, его можно использовать для проверки, принимает ли конкретный вызов constexpr функции ветвь константного выражения:

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
}
(since C++23)

Конструкторы constexpr разрешены для классов, которые не являются литеральными типами. Например, конструктор по умолчанию std::shared_ptr является constexpr, что позволяет постоянную инициализацию .

Ссылочные переменные могут быть объявлены как constexpr (их инициализаторы должны быть reference constant expressions ):

static constexpr int const& x = 42; // constexpr ссылка на const int объект
                                    // (объект имеет статическую продолжительность хранения
                                    //  благодаря продлению времени жизни статической ссылкой)

Несмотря на то, что try блоки и встроенный ассемблер разрешены в constexpr функциях, генерация исключений которые не перехватываются (начиная с C++26) или выполнение ассемблерного кода по-прежнему запрещено в константных выражениях.

Если переменная имеет константное разрушение, нет необходимости генерировать машинный код для вызова её деструктора, даже если её деструктор не является тривиальным.

Нелямбда, неспециальная и нетемплейтная constexpr функция не может неявно стать немедленной функцией. Пользователи должны явно пометить её consteval чтобы сделать такое определение функции корректным.

(начиная с 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) Изменение активного члена union при константной оценке
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 constexpr функции, вычисляющие факториалы; определяет литеральный тип, расширяющий строковые литералы:

#include <iostream>
#include <stdexcept>
// C++11 constexpr функции используют рекурсию вместо итерации
constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}
// C++14 constexpr функции могут использовать локальные переменные и циклы
#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; }
};
// C++11 constexpr функции должны были помещать всё в один оператор return
// (C++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 << "Количество строчных букв в \"Hello, world!\" равно ";
    constN<countlower("Hello, world!")> 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] << ' ';
    std::cout << '\n';
}

Вывод:

4! = 24
8! = 40320
Количество строчных букв в "Hello, world!" равно 9
Массив длиной 12 содержит элементы: 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
переменная не
становится встроенной
  1. Это избыточно, потому что не может быть более одного объявления шаблона переменной с constexpr спецификатором.

Смотрите также

constant expression определяет выражение , которое может быть вычислено во время компиляции
consteval specifier (C++20) указывает, что функция является немедленной функцией , то есть каждый вызов функции должен быть в константном вычислении
constinit specifier (C++20) утверждает, что переменная имеет статическую инициализацию, т.е. zero initialization и constant initialization
C documentation для constexpr