<cmath> 在 C++14 / C++11 中隐藏 Isnan<math.h>?
<cmath> hides isnan in <math.h> in C++14 / C++11?
我这里有一个小的测试应用程序,它使用来自<math.h>
isnan
:
#include <iostream>
#include <math.h>
int main()
{
double d = NAN;
std::cout << isnan(d) << 'n';
return 0;
}
在 3 种不同的标准下构建和运行:
$ 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;
}
构建并运行:
C++98作品
$ g++ -std=c++98 main.cpp; ./a.out 1 1
C++11 和 C++14 没有,isnan
找不到。
$ 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) ^
请注意,包含顺序并不重要。如果我在<math.h>
之前或之后包括<cmath>
,结果是相同的。
问题
- 为什么
isnan
走了? - 无需返回并更改旧代码以在新标准下编译,有没有办法解决此问题?
简要总结相关观点,主要来自Jonathan Wakely的优秀博客文章:
- glibc <2.23 的
math.h
声明了与 C99/C++11 版本(bool isnan(double);
(不兼容的过时的 X/Openint isnan(double);
。 - glibc 2.23 的
math.h
通过在 C++11 或更高版本中不声明isnan
函数来解决此问题。 - 它们仍然定义一个
isnan
宏。#include <cmath>
按照C++标准的要求核弹该宏。 - GCC 6 的 libstdc++ 提供了自己的特殊
math.h
标头,该标头在全局命名空间中声明bool isnan(double);
(除非 libcmath.h
声明过时的签名(,并根据标准要求对宏进行核弹。 - 在 GCC 6 之前,
#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
如果你从GCC查看内部<cmath>
,它有这个:
. . .
#include <math.h>
. . .
#undef isnan
这就是为什么顺序无关紧要的原因 - 每当您#include <cmath>
时,<math.h>
都会自动包含,并且其内容(部分(被核弹。
由于 #ifndef _MATH_H
,尝试再次包含它不会有任何效果。
现在,标准对这种行为有什么看法?
[depr.c.headers]:
。每个 C 标头,每个 其中有一个形式
name.h
的名称,其行为就像每个名称放置一样 在标准库命名空间中,相应的 C名称标头为 放置在全局命名空间范围内。目前尚不清楚是否 这些名称首先在命名空间范围内声明或定义 ([basic.scope.namespace]( 的命名空间std
,然后注入 通过显式 using 声明进入全局命名空间范围 ([命名空间.udecl](。[ 示例:标头
<cstdlib>
确保提供其声明 和命名空间中的定义std
.它还可以提供这些 全局命名空间中的名称。标题肯定<stdlib.h>
在全局 命名空间,就像在 C 标准中一样。它还可能提供这些名称 在命名空间std
. — 结束示例 ]
因此,<cmath>
不在全局命名空间中提供isnan
是可以的。
但是当两者都包含在一个编译单元中时会发生什么是一个灰色地带,尽管有人可能会争辩说上面的陈述意味着两个版本都必须互操作,在这种情况下,这将是GCC/libstdc++(某些版本(中的一个错误。
math.h
中的许多函数实际上是宏。由于这不是惯用的 c++,因此标头cmath
包含以下代码:
...
#undef isinf
#undef isnan
#undef isnormal
...
然后将所有这些未定义的宏实现为namespace std
中的函数。至少对于 gcc 6.1.1 来说是正确的。这就是为什么您的编译器找不到isnan
的原因。
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 呼叫运营商<<临时
- 如何防止clang格式在流运算符调用之间添加换行符<<
- <<操作员在下面的行中工作
- EASTL矢量<向量<int>>连续的
- C - 创建矢量&lt; vector&lt; double&gt;&gt;矩阵具有分配而不是inizializ
- 为什么将此对向量&lt; map&lt; int,int&gt;&gt;中的地图进行更新.失败
- C :对矢量进行排序&lt; struct&gt;(结构有2个整数)基于结构的整数之一
- 明确的专业化“ CheckIntmap&lt;&gt;”实例化
- 什么是模板&lt;&gt;inline bla bla
- 编辑C Qlist&lt; object*&gt; gt;QML代码和一些QML警告中的模型
- eigen :: llt&lt;eigen :: matrixxd&gt;具有不完整的类型
- 错误,包括&lt; ctype&gt;在原子上使用C 11
- std::vector<;uint8_t>;当C++11/14启用时,手动复制而不是调用memcpy
- 如何加入向量&lt; int&gt;到C 中的单个INT
- 是std :: set&lt; std :: future&gt;不可能存在
- 是numeric_limits&lt; int&gt; :: is_modulo从逻辑上矛盾
- opencv 2.4.7在iOS错误背景_segm.hpp #include&lt; list&gt;未找到
- 在修改列表后,std :: list&lt; t&gt; :: end()的值是否会更改
- ///<评论></评论>在Visual Studio中