为什么isnan含糊不清以及如何避免它?

vit*_*aut 10 c++ gcc compiler-errors cmath c++11

既然isnan可以是宏(在C++ 98中)或在命名空间std中定义的函数(在C++ 11中),这个简单的例子说明了编写在两种情况下都有效的代码的明显(并且可能是天真的)方式.

#include <cmath>

int main() {
  double x = 0;
  using namespace std;
  isnan(x);
}
Run Code Online (Sandbox Code Playgroud)

但是,编译它会在GCC(使用-std = c ++ 11)和Clang中产生错误:

test.cc: In function ‘int main()’:
test.cc:6:10: error: call of overloaded ‘isnan(double&)’ is ambiguous
   isnan(x);
          ^
test.cc:6:10: note: candidates are:
In file included from /usr/include/features.h:374:0,
                 from /usr/include/x86_64-linux-gnu/c++/4.8/bits/os_defines.h:39,
                 from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h:426,
                 from /usr/include/c++/4.8/cmath:41,
                 from test.cc:1:
/usr/include/x86_64-linux-gnu/bits/mathcalls.h:234:1: note: int isnan(double)
 __MATHDECL_1 (int,isnan,, (_Mdouble_ __value)) __attribute__ ((__const__));
 ^
In file included from test.cc:1:0:
/usr/include/c++/4.8/cmath:626:3: note: constexpr bool std::isnan(long double)
   isnan(long double __x)
   ^
/usr/include/c++/4.8/cmath:622:3: note: constexpr bool std::isnan(double)
   isnan(double __x)
   ^
/usr/include/c++/4.8/cmath:618:3: note: constexpr bool std::isnan(float)
   isnan(float __x)
   ^
Run Code Online (Sandbox Code Playgroud)

为什么在C++ 11中这是不明确的,以及如何使它适用于C++ 98和C++ 11,最好不需要太多的条件编译?

Sha*_*our 12

这是一个libstdc++错误报告中记录的错误std函数与C函数冲突时使用c ++ 0x支持(并使用命名空间std)和一个非常类似于OP的再现示例:

#include <stdlib.h>
#include <cmath>
#include <stdio.h>

using namespace std;

int main(int argc, char** argv)
{
    double number = 0;
    if (isnan(number))
    {
        printf("Nan\n");
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

其中一条评论说:

我不认为这是问题所在,因为libstdc ++总是在全局命名空间中声明名称,即使它在C++ 03中无效 - 我们没有为C++ 0x更改它(所有发生的事情都是放宽标准以反映实际实施的现实)

这可能最终得到解决,直到那时bug报告提供的解决方案如下:

通过调用:: isnan或std :: isnan来明确限定isnan

使用::isnan尽可能我可以告诉作品预C++ 11C++中11.

当然这是一个libstdc++特定的解决方案,它看起来也很有效,libc++但如果你需要支持一个不起作用的编译器,你可能不得不求助于使用#if/#else.

注意,如具有isnan标记的constexpr 的MM表示不符合,这也是已知问题,尽管它对该特定问题没有贡献.

另请参阅相关的错误报告:[C++ 11]重载'isnan'的调用是不明确的,使用bool返回类型识别内置函数.第二部分讨论可能的libstdc++解决方

更新

如果你想要一个gcc/clang解决方案,看起来它们都支持__builtin_isnan,请参阅内置的gcc文档以获取更多信息.另请参阅这个glibc 错误报告,用内置替换isnan等.


5go*_*der 5

在考虑了一段时间之后,我认为在C ++ 11之前根本没有可移植的方法来执行此操作。C isnan宏是在C99中引入的,但是C ++ 98和C ++ 03是基于C89的。因此,如果您依靠C ++ 98/03的实现拖入提供isnan(顺便说一句,这是不合格的)C99标头,则无论如何您都在做出不可移植的假设。

然后unsing,用using声明替换该指令,将为您提供以下代码,这些代码是可移植的C ++ 11(也可解决libstdc ++的缺陷),并且可能适用于较早实现的情况。(无论它们isnan是作为全局宏提供还是作为函数提供namespace)。

template <typename T>
bool
my_isnan(const T x)
{
#if __cplusplus >= 201103L
  using std::isnan;
#endif
  return isnan(x);
}
Run Code Online (Sandbox Code Playgroud)

将其包装在其自身的功能中似乎可以#if接受。