Подтвердить что ты не робот

Что происходит, когда исключение генерируется при вычислении constexpr?

При вычислении постоянных выражений для инициализации a constexpr можно исключать исключения. Например, вот пример, когда вычисление постоянного выражения защищено от переполнения:

#include <iostream>
#include <stdexcept>

constexpr int g(int n, int n0, int n1) {
    return n == 0? n1: g(n - 1, n1, n0 + n1);
}

constexpr int f(int n) {
    return n < 42? g(n, 0, 1): throw std::out_of_range("too big");
}

int main()
{
    try {
        constexpr int f41 = f(41); // OK: constexpr
        int           f43 = f(43); // OK: throws an exception
        constexpr int f42 = f(42); // not OK but what happens?
    }
    catch (std::exception const& ex) {
        std::cout << "ERROR: " << ex.what() << "\n";
    }
}

Первый вызов f() просто показывает, что a constexpr может быть вычислен. Второй вызов f() не используется для инициализации constexpr и генерирует исключение во время выполнения. Третий вызов f() используется для инициализации constexpr, но эта точка никогда не будет достигнута, потому что выбрано исключение. Однако что должно произойти в этом случае? Я ожидаю, что обработчик в catch -clause будет выполнен, но gcc и clang создайте ошибку времени компиляции.

4b9b3361

Ответ 1

Инициализатор для переменной constexpr должен быть константным выражением (С++ 11 §7.1.5/9):

A constexpr спецификатор, используемый в объявлении объекта, объявляет объект как const. Такой объект должен иметь буквальный тип и должен быть инициализирован. Если он инициализируется вызовом конструктора, [...]. В противном случае, или если спецификатор constexpr используется в декларации ссылки, каждое полное выражение, которое появляется в его инициализаторе, должно быть постоянным выражением.

Обратите внимание на следующие требования для постоянного выражения (§5.19/2):

Условное выражение является выражением основной константы, если оно не связано с одним из следующих как потенциально оцениваемое подвыражение, но подвыражения [...] условных операций, которые не оцениваются, не считаются

  • [...]

  • вызов функции constexpr с аргументами, которые при замене подстановкой вызова функции (7.1.5) не вызывают константное выражение;

  • [...]

  • выражение throw (15.1).

Подстановка функции для функции constexpr определяется следующим образом (§7.1.5/5):

Подстановка функции вызова для вызова функции constexpr [...] означает неявное преобразование каждого аргумента в соответствующий тип параметра, как если бы с помощью инициализации копирования, подставляя это преобразованное выражение для каждого использования соответствующего параметра в function-body и [...], неявно преобразуя полученное возвращаемое выражение или braced-init-list в возвращаемый тип функции, как если бы это произошло путем инициализации копии. Такая замена не меняет значения.

Как мы видели выше (§5.19/2), подвыражения условных операций, которые не оцениваются, не рассматриваются. f(42) не является постоянным выражением, потому что, когда вы выполняете подстановку вызова функции на f, это приводит к выражению с выражением throw на стороне оцениваемой условной операции. С другой стороны, для f(41) throw заканчивается на стороне, которая не оценивается.

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