Как проверить, что код выполняется во время компиляции (compile-time)?

«Как проверить, что код выполняется во время компиляции (compile-time)?» — вопрос из категории C++ Core, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В современном C++ для гарантии вычислений во время компиляции используются constexpr (C++11/14), consteval (C++20) и static_assert для проверки.

1. Использование constexpr и static_assert: Если constexpr-функция не может быть вычислена на этапе компиляции, код не скомпилируется.

constexpr int factorial(int n) {
    int result = 1;
    for (int i = 2; i <= n; ++i) { // Циклы разрешены в constexpr с C++14
        result *= i;
    }
    return result;
}

// Проверка во время компиляции
static_assert(factorial(5) == 120, "Compile-time computation failed");
static_assert(factorial(0) == 1);

int main() {
    constexpr int compile_time_value = factorial(6); // Вычисляется при компиляции
    int runtime_value = factorial(10); // Может быть вычислено и во время выполнения
    return 0;
}

2. Использование consteval (C++20): Функции, помеченные как consteval, должны давать результат на этапе компиляции. Вызов такой функции с аргументами, известными только во время выполнения, приведёт к ошибке компиляции.

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

static_assert(square(9) == 81); // OK

int main() {
    constexpr int a = square(5); // OK
    int runtime_input = 7;
    // int b = square(runtime_input); // Ошибка компиляции: аргумент не является константным выражением
    return 0;
}

3. Проверка типов и условий на этапе компиляции с if constexpr:

template<typename T>
constexpr auto getTypeSize() {
    if constexpr (std::is_integral_v<T>) {
        return sizeof(T);
    } else {
        // Этот branch отбрасывается на этапе компиляции для целочисленных типов
        return 0;
    }
}

static_assert(getTypeSize<int>() == 4); // Проверка для 32-битной платформы

Практическое применение:

  • Вычисления констант: Размеры массивов, математические константы.
  • Метапрограммирование: Генерация типов, проверка условий.
  • Оптимизация: Вынос вычислений из времени выполнения, если аргументы известны при компиляции.

Если static_assert срабатывает, вы получаете ошибку компиляции — это и есть доказательство, что проверка (или вычисление) произошла на этапе компиляции.