В чем разница между constexpr и const в C++?
Ключевое различие: время вычисления
Главное отличие между const и constexpr в C++ — это когда вычисляется значение. const гарантирует, что переменная не будет изменена после инициализации, но значение может быть вычислено во время выполнения. constexpr требует, чтобы значение было вычислено на этапе компиляции (compile-time), если это возможно.
Проще говоря: const — это "я обещаю не менять эту переменную". constexpr — это "это значение должно быть известно компилятору до запуска программы".
Пример с переменными
int get_value() {
return 42; // Может быть любым вычислением
}
int main() {
const int a = get_value(); // ОК: a не изменится, но значение вычисляется в runtime
// constexpr int b = get_value(); // Ошибка компиляции! get_value() не constexpr
const int c = 5; // ОК: константа времени компиляции
constexpr int d = 5; // ОК: то же самое
}
Функции: самое интересное
С функциями разница становится критической. constexpr функция может быть вычислена на этапе компиляции, если все её аргументы — константы времени компиляции. Если нет — она работает как обычная функция.
constexpr int factorial(int n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
int main() {
constexpr int f5 = factorial(5); // Вычислено на этапе компиляции! f5 = 120
int x = 5;
int f_x = factorial(x); // Вычислено в runtime, но работает
int arr[ factorial(4) ]; // ОК: размер массива известен во время компиляции
// int arr2[ factorial(x) ]; // Ошибка: x не constexpr
}
Когда использовать что?
- Используйте
constдля переменных, которые не будут меняться, но значение может быть получено динамически (например, из пользовательского ввода или вычислений в runtime). - Используйте
constexprкогда вам нужно гарантировать, что значение известно на этапе компиляции. Это необходимо для:- Размеров массивов (в старом C++), шаблонных параметров (например,
std::array<int, N>) - Оптимизации — константы времени компиляции могут быть встроены в код без вызова функции
- Метапрограммирования — вычисления во время компиляции сложных значений
- Размеров массивов (в старом C++), шаблонных параметров (например,
Важное замечание про C++17 и C++20
В C++17 constexpr лямбды стали возможны. В C++20 constexpr функции могут содержать try-catch, dynamic_cast и даже виртуальные вызовы (в ограниченных случаях). Также появились constexpr векторы и строки (но с ограничениями).
На практике: если вы пишете функцию, которая может быть вычислена на этапе компиляции, делайте её constexpr. Компилятор сам решит, вызывать её в compile-time или runtime. Для переменных — используйте constexpr если значение точно известно на этапе компиляции, и const в остальных случаях.
Дополнение: constexpr в контексте оптимизации
Ещё один важный аспект — constexpr позволяет выполнять "вычисления во время компиляции" (compile-time computation), что может значительно ускорить программу. Например, если у вас есть таблица синусов, которую нужно вычислить один раз, вы можете сделать это на этапе компиляции:
constexpr double sin_table[360] = { /* вычисления */ };
Это особенно полезно для встраиваемых систем или игр, где каждый такт процессора на счету. const такой гибкости не даёт — он лишь запрещает изменение, но не гарантирует вычисление на этапе компиляции.
Также стоит отметить, что constexpr переменные по умолчанию имеют внутреннее связывание (internal linkage), как и const с глобальной областью видимости. Но если вы используете extern constexpr, это вызовет ошибку — constexpr не может быть объявлен с внешним связыванием, потому что его значение должно быть известно в каждой единице трансляции.
На практике: если вы сомневаетесь, используйте const для переменных и constexpr для функций, которые могут быть вычислены на этапе компиляции. Это безопасный и эффективный подход.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
Как начать изучать Python с нуля: лучшая стратегия + 17 шагов для самообучения