关于c ++:隐藏了C ++ 14 / C ++ 11中中的isnan?

hides isnan in in C++14 / C++11?

我这里有一个小测试应用程序,它使用中的isnan

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <math.h>

int main()
{
    double d = NAN;

    std::cout << isnan(d) << '
'
;

    return 0;
}

根据3种不同标准构建和运行:

1
2
3
4
5
6
7
8
$ 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

现在我们还包括,并使用isnanstd::isnan进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <cmath>
#include <math.h>

int main()
{
    double d = NAN;

    std::cout << std::isnan(d) << '
'
;
    std::cout << isnan(d) << '
'
;

    return 0;
}

构建并运行:

C ++ 98可以工作

1
2
3
$ g++ -std=c++98 main.cpp; ./a.out
1
1

C ++ 11和C ++ 14没有找到,isnan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ 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) << '
'
;
                         ^
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) << '
'
;
                         ^
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)
     ^

请注意,包含的顺序并不重要。 如果我在之前或之后包含,结果是相同的。

问题

  • 为什么isnan消失了?
  • 无需返回并更改旧代码以在新标准下编译,有没有办法解决这个问题?


简要总结相关要点,主要来自Jonathan Wakely的优秀博客文章:

  • glibc <2.23的math.h声明过时的X / Open int isnan(double);与C99 / C ++ 11版本(bool isnan(double);)不兼容。
  • glibc 2.23的math.h通过不在C ++ 11或更高版本中声明isnan函数来解决这个问题。
  • 所有这些仍然定义了一个isnan宏。 #include 根据C ++标准的要求核对该宏。
  • GCC 6的libstdc ++提供了自己的特殊math.h头,它在全局命名空间中声明了一个bool isnan(double);(除非libc math.h声明了过时的签名),并且还按照标准的要求来核对宏。
  • 在GCC 6之前,#include 只包含libc中的标题,因此宏不会被破坏。
  • #include 总是核心宏。

净结果,在C ++ 11模式下:

1
2
3
4
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查看,它有:

1
2
3
4
. . .
#include <math.h>
. . .
#undef isnan

这就是订单无关紧要的原因 - 无论何时#include 都是自动包含的,其内容是(部分)核心的。

由于#ifndef _MATH_H,尝试再次包含它将无效。

现在,标准对这种行为有什么看法?

[depr.c.headers]:

... every C header, each
of which has a name of the form name.h, behaves as if each name placed
in the standard library namespace by the corresponding cname header is
placed within the global namespace scope. It is unspecified whether
these names are first declared or defined within namespace scope
([basic.scope.namespace]) of the namespace std and are then injected
into the global namespace scope by explicit using-declarations
([namespace.udecl]).

[ Example: The header assuredly provides its declarations
and definitions within the namespace std. It may also provide these
names within the global namespace. The header assuredly
provides the same declarations and definitions within the global
namespace, much as in the C Standard. It may also provide these names
within the namespace std. — end example ]

所以在全局命名空间中不提供isnan是可以的。

但是当它们都被包含在一个编译单元中时,它应该是一个灰色区域,尽管可以说上面的语句暗示两个版本必须互操作,在这种情况下它将是GCC / libstdc ++(某些版本)中的错误。


math.h中的许多函数实际上都是宏。由于这不是惯用的c ++,标头cmath包含以下代码:

1
2
3
4
5
    ...
    #undef isinf
    #undef isnan
    #undef isnormal
    ...

然后将所有那些未定义的宏实现为namespace std中的函数。对于gcc 6.1.1,这至少是正确的。这就是你的编译器找不到isnan的原因。