X Tutup
The Wayback Machine - https://web.archive.org/web/20221103184733/https://ru.cppreference.com/w/cpp/language/constexpr
Пространства имён
Варианты
Действия

Спецификатор constexpr (начиная с C++11)

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итераций (циклы)
Операторы переходов
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++20)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
const/volatile
constexpr (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определённые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Типы
Объявление typedef
Объявление псевдонима типа (C++11)
Приведения
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции, зависящие от класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
Обьявления
Объявления
ссылка
указатель
массив
Block declarations
простое объявление
объявление структурных привязок (C++17)
объявление псевдонимов(C++11)
объявление псевдонимов пространств имён
using-declaration
директива using
объявление static_assert (C++11)
определение asm
объявление непрозрачного enum(C++11)
Другие объявления
определение пространств имён
объявление функции
объявление шаблона класса
объявление шаблона функции
явное инстанцирование шаблона(C++11)
явная специализация шаблона
спецификация связывания
объявление атрибута (C++11)
пустое объявление
 

Содержание

[править] Объяснение

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

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

Переменная constexpr должна соответствовать следующим требованиям:

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

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

(начиная с C++20)

Функция constexpr должна соответствовать следующим требованиям:

(до C++20)
(начиная с C++20)
  • её возвращаемый тип (если есть) должен быть LiteralType
  • для конструктора и деструктора (начиная с C++20), класс не должен иметь виртуальных базовых классов
  • существует по крайней мере один набор значений аргументов, таких что вызов функции может быть оценённым подвыражением основного константного выражения (для конструкторов достаточно использовать константный инициализатор) (начиная с C++14). Для нарушения этого пункта диагностика не требуется.
  • тело функции должно быть либо удалено, либо установлено по умолчанию, либо содержать только следующее:
(до C++14)
  • тело функции не должно содержать:
  • оператор goto
  • оператор с меткой, отличной от case и default
(до C++20)
  • определение переменной нелитерального типа
  • определение переменной со статической или потоковой длительностью хранения
(Тело функции, которая =default; или =delete;, не содержит ничего из вышеперечисленного.)
(начиная с C++14)
(до C++23)

Конструктор constexpr, тело функции которого не является =delete;, должен соответствовать следующим дополнительным требованиям:

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

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

(до C++20)

Деструктор constexpr, тело функции которого не является =delete;, должен соответствовать следующим дополнительным требованиям:

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

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

[править] Примечание

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

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
                                    // (объект имеет статическую длительность хранения
                                    //  из-за продления жизни по статической ссылке)

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

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

(начиная с 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 ограничений
__cpp_constexpr_in_decltype 201711L (C++11)
(DR)
Генерация определений функций и переменных, когда это необходимо для константной оценки
__cpp_constexpr_dynamic_alloc 201907L (C++20) Операции для динамической длительности хранения в функциях constexpr

[править] Ключевые слова

constexpr

[править] Пример

Определение constexpr функции C++11, которая вычисляет факториалы, и литеральный тип, расширяющий строковые литералы:

#include <iostream>
#include <stdexcept>
 
// Функции constexpr С++11 используют рекурсию, а не итерацию
// (Функции constexpr С++14 могут использовать локальные переменные и циклы)
constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}
 
// литеральный класс
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
}

Вывод:

4! = 24
8! = 40320
количество строчных букв в \"Привет, мир!\" равно 8

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам 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) утверждает, что переменная имеет статическую инициализацию, то есть инициализация нулём и константная инициализация[править]
X Tutup