Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different behavior of MSVC and clang for if constexpr branches

Tags:

c++

Given this helper function:

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 "";
}

which is supposed to convert basic types (and strings) to a string expression, I get a compilation error with MSVC when using it like this:

int main() {
  std::string temp = toString(true);
  return 0;
}

With clang this compiles without any problem, with MSVC however I get this:

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> ]

Obviously the compiler considers the if constexpr (std::is_arithmetic<Type>::value) test as valid alternative too and generates the mentioned error. However, at runtime it correctly takes the path for bool (when I leave out the if constexpr (std::is_arithmetic<Type>::value) part or use a cast if (std::isnan(static_cast<double>(value)))).

How can I make this compile correctly on Windows as well?

like image 374
Mike Lischke Avatar asked May 29 '19 08:05

Mike Lischke


2 Answers

For bool at least two type traits return true:

std::is_same<bool, Type>::value
std::is_arithmetic<Type>::value

and then you make a call std::isnan(true). Use 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 "";
like image 193
Evg Avatar answered Nov 04 '22 03:11

Evg


std::isnan and std::isinf seemingly internally calls fpclassify in MSVC. This function is overloaded for floating-point types, and you pass an argument of type bool, thus the call is ambiguous.

To avoid this, you may cast the arguments, e.g., to 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";
  }

Live demo: https://godbolt.org/z/W7Z3r3


UPDATE

This seems to be a bug in MSVC implementation, since, according to cppreference, there should be an overload for integral arguments that behaves the same as the double overload. Minimal example:

auto b = std::isnan(1);

Live demo: https://godbolt.org/z/qcTfQs

like image 36
Daniel Langr Avatar answered Nov 04 '22 05:11

Daniel Langr