为什么 g++ 4.9.0 默认有 std::isnan?

Why does g++ 4.9.0 have std::isnan by default?

提问人: 提问时间:6/10/2017 更新时间:6/10/2017 访问量:360

问:

我的理解是该函数仅在 C++11 及更高版本中可用。此外,除非特别告知不要使用。std::isnang++-std=gnu++98

那么为什么要编译呢?

>> cat test.cpp
#include <cmath>
int main(int argc, char** argv)
{
    return std::isnan(0);
}

>> g++ test.cpp

是否有标志可以取出函数?<cmath>

C ++11 G++ G++4.9

评论

3赞 NathanOliver 6/10/2017
可能是一个扩展,因为它存在于 C99 中。如果使用 编译会发生什么?-pedantic
0赞 6/10/2017
@NathanOliver 它仍然使用 .如果它使用 C99 宏,它不应该在全局命名空间中吗?-pedantic
0赞 NathanOliver 6/10/2017
这些 C 宏实际上是按照 C++ 标准作为函数实现的(请参阅此答案)。有趣的是,即使在 gcc 4.4.7 中,您在程序集中也看到了 -std=c++98。__gnu_cxx::__enable_if<std::__is_arithmetic<int>::__value, int>::__type std::isnan<int>(int)

答:

5赞 Yakk - Adam Nevraumont 6/10/2017 #1

编译器开发人员懒于完全删除仅在下一个版本的标准中才可用的所有功能,尤其是当相关库 (C99) 同时拥有它时。

效用(验证你的代码是否真正遵循一个特定的标准)不够高,编译器编写者无法非常努力地让他们的编译器提供该服务。

相反,通常发生的事情是在特定的标准标志下实现新功能。有时它们会意外地向后移植。当标准最终确定时,部分实现会存在一段时间,直到它变得足够好。

然后开始研究标准的下一个版本。下一个版本标志为您提供了一个不太稳定的开发环境,因为新功能会被试用、丢弃和更改。

它为不向后移植东西付出了一些努力,但这并不是一个障碍。

评论

0赞 Lightness Races in Orbit 6/10/2017
基本上就是这样。无论你使用什么编译器,如果你期望它严格遵守特定的语言标准,你都会感到失望。
0赞 Swift - Friday Pie 6/10/2017
这些功能先于添加到标准中。它们以各种形式存在预示着标准。VC2010 和 gcc 4.6 都有这些。但是其他编译器或不同的平台有不同的命名方式,例如在Solaris上,直到C++11它才成为std命名空间的一部分,后来它只是重新导入到命名空间中,因此标准和非标准符号都存在。标准不否认向后兼容性或扩展
0赞 Yakk - Adam Nevraumont 6/10/2017
@Swift 正如它所预言的那样,会的。std::isnan
2赞 user743382 6/10/2017 #2

这是一个长期存在的问题,记录在常见问题解答中,但以一种您不一定能够理解的方式。

4.3. 并且总是被定义?_XOPEN_SOURCE_GNU_SOURCE

在 Solaris 上,g++(但不是 gcc)始终定义预处理器宏。在 GNU/Linux 上,同样的情况也发生在 .(这不是一个详尽的列表;其他宏和其他平台也会受到影响。_XOPEN_SOURCE_GNU_SOURCE

这些宏通常用于 C 库标头中,以保护新版本的函数免受旧版本的影响。C++ 标准库包括 C 标准库,但它需要 C90 版本,由于向后兼容的原因,对于许多供应商来说,这通常不是默认版本。

更重要的是,C++ 标准要求的行为仅在定义某些符号后在某些平台上可用。通常,该问题涉及与 I/O 相关的 typedef。为了确保正确性,编译器只需预定义这些符号即可。

请注意,仅当构建库时(在安装期间)对他们来说是不够的。由于我们没有“export”关键字,因此大部分库都以标头形式存在,这意味着在解析和编译程序时也必须定义符号。#define

要查看定义了哪些符号,请在目标的 gcc 配置标头中查找(并尝试更改它们以查看构建复杂代码时会发生什么)。您还可以运行 g++ -E -dM - < /dev/null“ [sic] 来显示任何特定安装的预定义宏列表。CPLUSPLUS_CPP_SPEC

这在邮件列表上已经讨论了很多

这种方法有点像疣。我们想找到一个更清洁的解决方案,但还没有人贡献时间。

解释一下:

glibc 是提供标准 C 库的东西。它支持多种模式。

它支持各种严格的C模式和严格的POSIX模式。在这些模式下,当仅包含标准标头时,只有标准功能可用。在 C90 模式下,这不包括 .isnan

它支持多种扩展模式。在这些模式下,非标准功能也可用。在 C90 + 扩展模式下,这包括 .预处理器宏是启用所有扩展的宏。isnan_GNU_SOURCE

libstdc++ 是提供标准 C++ 库的。它对 glibc 的要求比严格的 C90 模式提供的要多。因此,只有两个选项:要么 libstdc++ 不提供它无法提供的标准 C++ 功能,要么 libstdc++ 即使在严格的 ANSI 模式下也强制启用 glibc 的扩展。两者都意味着不符合 C++ 标准,但前者限制了功能,而后者提供了功能。后者被视为较小的邪恶。

解决此问题的唯一合理方法是让 glibc 提供一些非标准的方式来访问其扩展,即使在严格的 ANSI 模式下也是如此。目前还没有这样的方法,在创建这种方法之前,即使在标准的 C++ 编译模式下,非标准名称也将可用。