Учитывая эту вспомогательную функцию:
template<typename Type>
std::string toString(Type const& value, bool encloseInQuotes = false) {
if constexpr (std::is_same<bool, Type>::value) {
auto s = value ? "true" : "false";
return encloseInQuotes ? "\""s + s + "\"" : s;
}
if constexpr (std::is_arithmetic<Type>::value) {
if (std::isnan(value)) {
return encloseInQuotes ? "\"NaN\"" : "NaN";
}
}
return "";
}
который должен преобразовывать базовые типы (и строки) в строковое выражение, я получаю ошибку компиляции с MSVC при использовании его следующим образом:
int main() {
std::string temp = toString(true);
return 0;
}
С clang это компилируется без проблем, однако с MSVC я получаю следующее:
2>c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(403): error C2668: 'fpclassify': ambiguous call to overloaded function
2>c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(288): note: could be 'int fpclassify(long double) noexcept'
2>c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(283): note: or 'int fpclassify(double) noexcept'
2>c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(278): note: or 'int fpclassify(float) noexcept'
2>c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(403): note: while trying to match the argument list '(_Ty)'
2> with
2> [
2> _Ty=int
2> ]
2>: note: see reference to function template instantiation 'bool isnan(_Ty) noexcept' being compiled
2> with
2> [
2> Type=int,
2> _Ty=int
2> ]
Очевидно, что компилятор также рассматривает тест if constexpr (std::is_arithmetic<Type>::value)
как допустимую альтернативу и генерирует указанную ошибку. Однако во время выполнения он правильно выбирает путь для bool (когда я пропускаю часть if constexpr (std::is_arithmetic<Type>::value)
или использую приведение if (std::isnan(static_cast<double>(value)))
).
Как я могу правильно скомпилировать эту компиляцию и в Windows?
Для bool
как минимум две черты типа возвращают true
:
std::is_same<bool, Type>::value
std::is_arithmetic<Type>::value
а потом звонишь std::isnan(true)
. Используйте else if
:
if constexpr (std::is_same<bool, Type>::value) {
auto s = value ? "true" : "false";
return encloseInQuotes ? "\""s + s + "\"" : s;
}
else if constexpr (std::is_arithmetic<Type>::value) {
if (std::isnan(value)) {
return encloseInQuotes ? "\"NaN\"" : "NaN";
}
...
}
else
return "";
Это вина исходного кода OP. Что, если это int
, а не nan
? Он ничего не возвращает.
@SombreroChicken Это немного странно, поскольку, согласно en.cppreference.com/w/cpp/numeric/math/isnan, должна быть перегрузка для целочисленных типов, которая должна вести себя так же, как перегрузка для double
. Минимальный пример: godbolt.org/z/qcTfQs.
@DanielLangr, тогда вам следует поставить еще один if constexpr
, прежде чем пытаться вызывать isnan
.
@DanielLangr, отсутствие этой перегрузки, вероятно, является ошибкой в реализации.
@SombreroChicken, это не ошибка, это следствие сокращения до минимального примера. Исходный код в этом отношении был в полном порядке.
@Evg Я хочу сказать, что проблема в первую очередь вызвана ошибкой в реализации MSVC (отсутствует перегрузка std::isnan
и std::isinf
для целочисленных типов), а не отсутствием else
, как предполагает ваш ответ.
Вот и я сейчас тоже так думаю.
@DanielLangr, это верно для этой проблемы особый. Но что, если бы у isnan
не было перегрузки для целочисленного типа? Затем вы должны использовать else if
s, чтобы исключить эту ветвь. Поэтому я думаю, что этот ответ по-прежнему актуален для вопроса, потому что кто-то другой может найти этот вопрос, пытаясь решить аналогичную проблему, но не совсем ту же.
@Evg Если вам нужно в ветке вызвать функцию, предназначенную только для аргументов с плавающей запятой, не лучше ли просто использовать if constexpr (std::is_floating_point_v<Type>)
? Тогда не нужно else if
.
@Evg Если вы проверяете арифметический тип, а затем вызываете функцию, которая не действительна для всех арифметических типов, это неправильный дизайн. Это не имеет ничего общего с наличием/отсутствием else
. Кстати, я не говорил, что ваш вопрос неактуален. Я думаю, что это.
@DanielLangr, моя цель состояла в том, чтобы продемонстрировать еще один момент: вы можете использовать else if
, чтобы исключить ветку, если вам нужно. В этом конкретном случае вы можете изменить код, чтобы получить еще лучшее решение. Я полностью согласен с этим.
std::isnan
и std::isinf
, по-видимому, внутренне вызывают fpclassify
в MSVC. Эта функция перегружена для типов с плавающей запятой, и вы передаете аргумент типа bool
, поэтому вызов — двусмысленный.
Чтобы избежать этого, вы можете привести аргументы, например, к double
:
if constexpr (std::is_arithmetic<Type>::value) {
if (std::isinf((double)value)) {
return encloseInQuotes ? "\"INF\"" : "INF";
}
if (std::isnan((double)value)) {
return encloseInQuotes ? "\"NaN\"" : "NaN";
}
Живая демонстрация: https://godbolt.org/z/W7Z3r3
ОБНОВИТЬ
Похоже, это ошибка в реализации MSVC, поскольку, согласно cppreference, должна быть перегрузка для целочисленных аргументов, которая ведет себя так же, как перегрузка double
. Минимальный пример:
auto b = std::isnan(1);
Живая демонстрация: https://godbolt.org/z/qcTfQs
Это то, что я сделал в попытке решить проблему, но это вообще не должно быть необходимо, так как логическая ветвь все равно берется для логического ввода. Почему MSVC вообще делает тип ввода int
? Это кажется мне компилятором.
@MikeLischke, но в этом проблема - вызывались обе ветки, потому что у вас не было else, а bool является одновременно арифметическим типом и таким же, как bool (очевидно). Так что оба, если бы были правдой.
@Steve И это проблема иметь несколько if constexpr
с истинным оценочным условием?
Да я вижу. Немного сложно проверить это полностью, так как clang не выдает предупреждение или ошибку для логических значений при использовании с std::isnan
или std::isinf
. Следовательно, я не могу точно сказать, оптимизирует ли clang это при компиляции возврата в ветке bool.
@MikeLischke Почему должно выдаваться предупреждение? bool
— целочисленный тип, поэтому он просто использует перегрузку для целочисленных аргументов, что является правильным поведением.
@MikeLischke, дело не в оптимизации. Если бы у clang не было перегрузки для целочисленных типов, вы бы получили ту же ошибку. Даже если функция не вызывается во время выполнения, компилятор должен иметь возможность генерировать вызов.
Это решение не будет работать, например, в случае, если
Type
выводится какint
. Это вызовет ту же самую ошибку: godbolt.org/z/u4e0HF.