C++14下GNU与Intel编译器对含NaN的constexpr表达式编译差异问题
问题成因
- 代码本身存在逻辑错误:
absoluteTolerance != std::numeric_limits<T>::quiet_NaN()这类写法不符合NaN的比较规则,NaN与任何值做等值比较都会返回false,因此这段判断无论输入是否为NaN都会成立,完全没有校验效果。 - Intel经典编译器(icpc 2021系列)的C++14模式对constexpr上下文的浮点比较有严格限制:即使NaN的等值比较逻辑无效,编译器也会直接禁止这类表达式出现在constexpr的编译期求值路径中,因此抛出编译错误。GCC对该场景做了放宽,因此可以正常编译。
可行解决方案
方案1:用编译器内置NaN判断函数修正逻辑(兼容C++14,同时支持GCC/ICC)
将原assert中的两行NaN等值比较替换为编译器内置的__builtin_isnan判断,该函数可在GCC和ICC的constexpr上下文正常工作,同时逻辑正确:
assert( absoluteTolerance > 0 && absoluteTolerance != std::numeric_limits<T>::infinity() && !__builtin_isnan(absoluteTolerance) );
修改后原代码可直接在两个编译器下正常编译,且能正确校验输入是否为NaN。
方案2:编译期分支规避(适用于输入均为已知合法编译期常量的场景)
如果你传入的容差参数都是编译期确定的合法值(比如示例中的toRadians(1.0)),本身不可能出现NaN,可以通过宏区分编译器,在icpc下跳过constexpr构造函数中的NaN判断,仅保留运行时校验:
constexpr ExpandedAbsoluteEqualityComparator(const T absoluteTolerance) : absoluteTolerance_(absoluteTolerance) { assert( absoluteTolerance > 0 && absoluteTolerance != std::numeric_limits<T>::infinity() #ifndef __INTEL_COMPILER && !__builtin_isnan(absoluteTolerance) #endif ); // 运行时额外校验NaN,不影响constexpr编译 if (!std::is_integral<T>::value) { // 仅浮点类型执行 assert(!std::isnan(absoluteTolerance)); } }
方案3:更换编译器
如果项目允许切换编译器,可将经典icpc替换为Intel新一代oneAPI DPC++/C编译器(icx),该编译器对C标准的兼容性更好,C++14模式下可直接编译原代码,无此错误。
内容的提问来源于stack exchange,提问作者steinmig
相关产品推荐
相关产品推荐

