Ste*_*mer 21 c++ nan cmath c++11 c++14
我这里有它使用一个小的测试应用程序isnan的<math.h>:
#include <iostream>
#include <math.h>
int main()
{
double d = NAN;
std::cout << isnan(d) << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
根据3种不同标准构建和运行:
Run Code Online (Sandbox Code Playgroud)$ g++ -std=c++98 main.cpp; ./a.out 1 $ g++ -std=c++11 main.cpp; ./a.out 1 $ g++ -std=c++14 main.cpp; ./a.out 1
现在我们还包括<cmath>,并测试两者isnan和std::isnan:
#include <iostream>
#include <cmath>
#include <math.h>
int main()
{
double d = NAN;
std::cout << std::isnan(d) << '\n';
std::cout << isnan(d) << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
构建并运行:
C++ 98可以工作
Run Code Online (Sandbox Code Playgroud)$ g++ -std=c++98 main.cpp; ./a.out 1 1
isnan找不到C++ 11和C++ 14 .
Run Code Online (Sandbox Code Playgroud)$ g++ -std=c++11 main.cpp main.cpp: In function ‘int main()’: main.cpp:10:25: error: ‘isnan’ was not declared in this scope std::cout << isnan(d) << '\n'; ^ main.cpp:10:25: note: suggested alternative: In file included from main.cpp:3:0: /usr/include/c++/5/cmath:641:5: note: ‘std::isnan’ isnan(_Tp __x) ^ $ g++ -std=c++14 main.cpp main.cpp: In function ‘int main()’: main.cpp:10:25: error: ‘isnan’ was not declared in this scope std::cout << isnan(d) << '\n'; ^ main.cpp:10:25: note: suggested alternative: In file included from main.cpp:3:0: /usr/include/c++/5/cmath:641:5: note: ‘std::isnan’ isnan(_Tp __x) ^
请注意,包含的顺序并不重要.如果我<cmath>在之前<math.h>或之后包括,结果是相同的.
问题
isnan不见了?T.C*_*.C. 19
简要总结相关要点,主要来自Jonathan Wakely的优秀博客文章:
math.h声明过时的X/Open int isnan(double);,其与C99/C++ 11版本不兼容(bool isnan(double);).math.h通过不在isnanC++ 11或更高版本中声明函数来解决这个问题.isnan宏.#include <cmath>核心是C++标准所要求的宏.math.h头文件,它bool isnan(double);在全局命名空间中声明了一个(除非libc math.h声明了过时的签名),并且还按照标准的要求来修改宏.#include <math.h>只需要包含libc中的标题,这样宏就不会被破坏了.#include <cmath> 总是核心宏.净结果,在C++ 11模式下:
glibc < 2.23, GCC < 6: <math.h> uses the macro; <cmath> uses obsolete signature
glibc >= 2.23, GCC < 6: <math.h> uses the macro; <cmath> results in error
glibc < 2.23, GCC >= 6: <math.h> and <cmath> use obsolete signature
glibc >= 2.23, GCC >= 6: <math.h> and <cmath> use standard signature
Run Code Online (Sandbox Code Playgroud)
如果<cmath>从GCC 看内部,它有:
. . .
#include <math.h>
. . .
#undef isnan
Run Code Online (Sandbox Code Playgroud)
这就是为什么顺序并不重要-只要你#include <cmath>,<math.h>是自包含和被(部分)核爆其内容.
试图再次包含它将无效,因为#ifndef _MATH_H.
现在,标准对这种行为有什么看法?
...每个C头,每个都有一个表单的名称
name.h,就好像每个由相应的c 名称头放在标准库名称空间中的名称放在全局名称空间范围内.未指定是否首先在命名空间的命名空间范围([basic.scope.namespace])中声明或定义这些名称std,然后通过显式using-declarations ([namespace.udecl])将这些名称注入全局命名空间范围.[示例:标头
<cstdlib>确实在命名空间中提供其声明和定义std.它还可以在全局命名空间中提供这些名称.标题<stdlib.h>肯定在全局命名空间中提供相同的声明和定义,就像在C标准中一样.它还可以在命名空间中提供这些名称std. - 结束例子]
所以<cmath>没有isnan在全局命名空间中提供它是可以的.
但是当它们都被包含在一个编译单元中时,它应该是一个灰色区域,尽管可以说上面的语句暗示两个版本必须互操作,在这种情况下它将是GCC/libstdc ++(某些版本)中的错误.