View Categories

constexpr

В версии C++11 появились константные выражения constexpr (constant expression) - этот модификатор подсказывает компилятору, что сущность может быть выполнена и вычислена на этапе компиляции, то есть буквально силами компилятора, а не скомпилированной и запущенной программы. Посмотрим, как оно работает.

Когда код выполняется компилятором, то это происходит в compile time - на этапе компиляции. Если код выполняется непосредственно во время работы программы - это называется runtime, по-русски так и называют - в рантайме

Константы и выражения #

constexpr-выражение может содержать только члены, значение которых известно на момент компиляции, и операции между ними:

  • Целые и float литералы (непосредственно цифры в коде), bool, nullptr, строковые литералы ("строки")
  • Другие constexpr-константы
  • Вызов (результат) constexpr-функций и методов классов
  • Численные параметры шаблонов
  • #define-константы с такими же правилами
  • Математические, логические и бинарные операции

Такое выражение может быть присвоено constexpr константе:

#define MY_CONST 3.14f
constexpr int myconstexpr = 5;

constexpr int cnstFunc() {
    return 10;
}

constexpr int myconst = myconstexpr + 123 * MY_CONST * cnstFunc();

Отличие от обычной константы состоит в том, что выражение в любом случае вычисляется компилятором, т.е. даже не попадает в программу после компиляции - только результат в виде числа. Современные IDE, например VS Code (урок), подсвечивают результат, если навести курсор на имя константы - очень удобно, можно отлаживать некоторые конструкции даже не компилируя программу, не говоря о том, чтобы её запускать.

Функции #

constexpr функция может быть вычислена компилятором, если вызывается с известными на момент компиляции аргументами. В то же время, она может вызываться и выполняться как обычная функция в рантайме.

В разных версиях C++ есть разные ограничения на сам код таких функций: чем старше версия - тем меньше ограничений. Рассмотрим версию C++11, т.к. в основном она используется по умолчанию для разработки под МК (AVR, ESP) на момент написания урока. constexpr функция:

  • Должна содержать только один return
  • Не может содержать for, while, do, if, switch, goto
  • Не может содержать вызовов других не-constexpr функций
  • Не может создавать локальные переменные
  • Не может работать с динамической памятью (new, delete)

А что же тогда она вообще может и как с этим работать? Можно:

  • Вызывать другие constexpr функции и классы
  • Использовать рекурсивный вызов вместо циклов
  • Использовать параметры функции вместо локальных переменных
  • Использовать тернарный оператор для ветвления

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

constexpr int factorial(int n) {
    return n ? (n * factorial(n - 1)) : 1;
}

constexpr int power(int base, int exp) {
    return exp ? (base * power(base, exp - 1)) : 1;
}

Полезный инструмент, и становится ещё более полезным в свежих версиях C++. Например вот проект constexpr CSV парсера на C++17.

Возможности в версиях #

Возможность C++11 C++14 C++17 C++20 C++23
Простые выражения (арифметика, return)
Вызовы других constexpr функций
Шаблонные constexpr функции
Использование классов с constexpr членами
Локальные переменные
Условные операторы (if, switch)
Циклы (for, while, do)
Вложенные функции
Использование try/catch
Динамическое выделение памяти (new, delete)
Использование виртуальных функций
Работа с std::vector, std::string

Полезные страницы #

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Прокрутить вверх