MSVC 和 clang for if constexpr 分支的不同行为

Different behavior of MSVC and clang for if constexpr branches

本文关键字:分支 constexpr clang for if MSVC      更新时间:2023-10-16

给定此辅助函数:

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:\程序文件 (x86)\Windows 工具包\10\include\10.0.10240.0\ucrt\math.h(403):错误 C2668:"fpclassify":对重载函数的不明确调用

2>c:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\ucrt\math.h(288): 注意:可能是'int fpclassify(long double) noexcept'

2>c:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\ucrt\math.h(283): 注意:或 'int fpclassify(double) noexcept'

2>c:\Program Files (x86)\Windows Kits\10\include\10.0.10240.0\ucrt\math.h(278): 注意:或 'int fpclassify(float) noexcept'

2>c:\程序文件 (x86)\Windows 工具包\10\include\10.0.10240.0\ucrt\math.h(403): 注意:尝试匹配参数列表"(_Ty)"时

2>

2> [

2> _Ty=整数

2> ]

2>:注意:请参阅正在编译的函数模板实例化"bool isnan(_Ty) noexcept"的参考

2>

2> [

2> 类型=整数,

2> _Ty=整数

2> ]

显然,编译器也将if constexpr (std::is_arithmetic<Type>::value)测试视为有效的替代方案,并生成上述错误。但是,在运行时,它正确地采用布尔值的路径(当我省略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 "";

std::isnanstd::isinf似乎在MSVC中内部调用fpclassify。此函数对于浮点类型是重载的,并且您传递了类型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 实现中的一个错误,因为根据 cpp 首选项,应该有一个与double重载行为相同的积分参数的重载。最小示例:

auto b = std::isnan(1);

现场演示:https://godbolt.org/z/qcTfQs