В 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 ++.
У меня есть идея другого обходного пути.
Есть: используйте вспомогательную функцию 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.
Я могу ответить только на часть вашего вопроса: есть ли обходной путь?
Есть: используйте помощника 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
требования, и если так, рассчитывает результат. Проверка потерпит неудачу, так как выражение технически недопустимо, но дополнительная вспомогательная переменная улучшит простое сворачивание констант, так что к тому времени, когда проверяется правильность, недействительных встроенных функций больше нет. Но, как я уже сказал, это предположение.
Кажется, это работает на 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
источник, чтобы взглянуть, но пока нет ответа на вопрос «почему».