constexpr vs const в C++: в чем разница? Полный гайд

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Вступление: вечная битва констант в C++

Если вы когда-нибудь писали на C++ (или даже только начинали его изучать), то наверняка сталкивались с const. Эта магическая приставка обещает, что значение переменной не изменится. Но потом пришёл C++11 и принёс с собой constexpr. И началась путаница.

На Stack Overflow вопрос "What's the difference between constexpr and const?" собрал сотни тысяч просмотров. Программисты всех мастей путают эти два понятия, используя их как синонимы. Но это большая ошибка!

В этом туториале мы раз и навсегда разберёмся, чем отличаются constexpr и const в C++. Вы узнаете:

  • Что такое константа времени компиляции и константа времени выполнения.
  • Почему const — это не всегда про производительность.
  • Как constexpr превращает C++ в современный и безопасный язык.
  • Когда стоит использовать каждую из этих конструкций.

Поехали! 🚀

Глава 1. const: обещание, а не гарантия

Начнём с базы. const в C++ — это квалификатор типа, который говорит компилятору: "Эй, это значение нельзя менять после инициализации". Звучит просто, но есть нюанс.

Пример 1: const в действии

#include <iostream>

int main() {
    const int x = 42;  // Обещание: x не изменится
    // x = 100;        // Ошибка компиляции — отлично!
    
    std::cout << x;   // Выведет 42
    return 0;
}

Всё красиво. Но что, если значение const переменной вычисляется только во время выполнения?

Пример 2: const, который вычисляется в runtime

#include <iostream>

int get_value() {
    return 42;  // Функция возвращает значение
}

int main() {
    const int y = get_value();  // Всё ещё const?
    // Но значение y известно только во время выполнения!
    
    // Попробуем использовать в массиве:
    // int arr[y];  // Ошибка! y — не константа времени компиляции!
    
    return 0;
}

Вот он, главный подвох! const защищает переменную от изменения, но не гарантирует, что её значение известно на этапе компиляции. Если значение получается из функции, оно вычисляется только при запуске программы.

Ключевой вывод: const — это контракт на неизменяемость, а не на вычислимость во время компиляции.

Глава 2. constexpr: константы, которые живут в коде

Здесь на сцену выходит constexpr. Этот спецификатор появился в C++11 и кардинально изменил подход к константам. constexpr означает: "Это значение должно быть вычислено во время компиляции". Или, как минимум, может быть вычислено.

Пример 3: constexpr переменная

#include <iostream>

int main() {
    constexpr int z = 42;  // Гарантия: значение известно в compile-time
    
    int arr[z];  // Работает! z — константа времени компиляции
    
    std::cout << sizeof(arr) / sizeof(arr[0]);  // Выведет 42
    return 0;
}

Разница очевидна: constexpr позволяет использовать переменную там, где компилятору нужно знать точное значение — например, для размера массива или шаблонных параметров.

Пример 4: constexpr функции

Но самое интересное — это функции. constexpr можно применять к функциям, и тогда они могут вычисляться во время компиляции, если все аргументы — тоже константы времени компиляции.

#include <iostream>

constexpr int square(int x) {
    return x * x;
}

int main() {
    constexpr int result = square(5);  // Вычисляется в compile-time!
    int arr[result];                   // Работает
    
    std::cout << result;  // 25
    
    int runtime_value = 5;
    int runtime_result = square(runtime_value);  // Тоже работает, но в runtime
    
    return 0;
}

Обратите внимание: constexpr функция может работать и в runtime, если ей передать неконстантные аргументы. Это делает её невероятно гибкой.

Важно: До C++14 constexpr функции были сильно ограничены — только один return. С C++14 и выше они могут содержать циклы, условия и даже переменные.

Глава 3. Сравнительная таблица: const vs constexpr

Давайте закрепим разницу в удобной таблице.

Характеристика const constexpr
Неизменяемость ✅ Да ✅ Да
Вычисление в compile-time ❌ Не гарантируется ✅ Гарантируется (если возможно)
Использование для массивов ❌ Только если значение известно в compile-time ✅ Всегда
Применение к функциям ❌ Не применимо ✅ Да (C++11+)
Применение к объектам классов ✅ Да (неизменяемый объект) ✅ Да (C++17+, с constexpr конструктором)
Производительность Средняя (может быть runtime) Высокая (вычисления на этапе компиляции)

Глава 4. Когда что использовать? Практические советы

Теперь, когда мы разобрали теорию, давайте перейдём к практике. Как выбрать между const и constexpr в реальном коде?

Сценарий 1: Простые константы

Если вам нужно просто объявить неизменяемую переменную с известным значением — используйте constexpr.

// Плохо
const double pi = 3.14159;

// Хорошо
constexpr double pi = 3.14159;  // Теперь это compile-time константа

Сценарий 2: Значение из внешнего источника

Если значение приходит из файла, базы данных или пользовательского ввода — constexpr не подходит. Используйте const.

#                

Блоги

Рекомендаций по книгам