__builtin_round не является константным выражением

В G ++ различные встроенные математические функции являются constexpr при определенных условиях. Например, следующие компиляции:

static constexpr double A = __builtin_sqrt(16.0);
static constexpr double B = __builtin_pow(A, 2.0);

Они не всегда constexpr, хотя, это зависит от аргумента. Например, __builtin_sqrt(NAN) приводит к ошибке компиляции при использовании в качестве константного выражения.

Но я попадаю в странный случай, когда мне кажется, что это должен быть constexpr, но это не так:

static constexpr double value () { return 1.23; }
static constexpr double result = __builtin_round(__builtin_sqrt(value()));

Это производит:

a.cpp:2:73: error: ‘__builtin_round(1.1090536506409416e+0)’ is not a constant expression
static constexpr double result = __builtin_round(__builtin_sqrt(value()));
^

Я пробовал варианты вышеуказанного кода, и я обнаружил, что:

  • __builtin_round имеет особую роль в проблеме. Замена его какой-нибудь другой встроенной математической функцией, такой как sqrt или же pow устраняет ошибку Так что казалось бы, что __builtin_round просто не хватает поддержки constexpr. Но…
  • Если value() заменяется буквальным 1.23, это тоже удаляет ошибку.
  • Удаление __builtin_sqrtоставив только __builtin_round(value()), тоже удаляет ошибку.

Я хотел бы знать, почему round ведет себя таким образом, и если есть какие-либо обходные пути.

НОТА. Мне известно, что встроенные математические функции с их консистентностью являются нестандартной функцией, специфичной для компилятора. Пожалуйста, не читайте мне лекций о том, как я не должен использовать это, или как я не должен пытаться делать математику времени компиляции. В моем случае, математика constexpr — важная функция, и у меня все хорошо в зависимости от G ++.

8

Решение

У меня есть идея другого обходного пути.

Есть: используйте вспомогательную функцию pass_through

template<typename T>
constexpr T&& pass_through (T&& t) { return static_cast<T&&>(t); }

используйте это так:

static constexpr double value () { return 1.23; }
static constexpr double result = __builtin_round(pass_through(__builtin_sqrt(value())));

Этот код скомпилирован без ошибок в G ++.

Я также согласен с мнением, что об этом вопросе следует сообщить в GCC.

3

Другие решения

Я могу ответить только на часть вашего вопроса: есть ли обходной путь?

Есть: используйте помощника constexpr переменная.

Хотя __builtin_round(__builtin_sqrt(value())) отклоняется как постоянное выражение, helper = value() принято, и после этого result = __builtin_round(__builtin_sqrt(helper)), С другой стороны, helper = __builtin_sqrt(value()) а потом result = __builtin_round(helper),

Это несоответствие показывает, что GCC явно способен оценивать выражение во время компиляции и готов рассматривать его как constexpr, Несмотря на то, что он может не найти поддержки в стандарте, возможно, стоит сообщить об этом как о запросе на улучшение в системе отслеживания ошибок GCC.

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

2

Кажется, это работает на g ++ 4.8.2:

static constexpr double value () { return 1.23; }
static constexpr double root(double x) { return sqrt(x);}
static constexpr double result = roundl(root(value()));

Интересно, если я заменю roundl с round компилятор жалуется:

error: ‘round(1.1090536506409416e+0)’ is not a constant expression

Это также работает:

static constexpr double value () { return 1.23; }
static constexpr double roundroot(double x) { return roundl(sqrt(x));}
static constexpr double result = roundroot(value());

Но опять же, только с roundl а не с round, Все это также верно при использовании соответствующих __builtin_ версии (которые они просто обернуть).

Я загружаю gcc источник, чтобы взглянуть, но пока нет ответа на вопрос «почему».

1
По вопросам рекламы [email protected]