c语言isnan,关于c ++:隐藏了C ++ 14 / C ++ 11中中的isnan? | 码农家园

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

#include

#include

int main()

{

double d = NAN;

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

';

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

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

#include

#include

#include

int main()

{

double d = NAN;

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

';

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

';

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) << '

';

^

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消失了?

无需返回并更改旧代码以在新标准下编译,有没有办法解决这个问题?

Clang是使用libc ++,还是与gcc相同的libstdc ++?

@ildjarn ldd表示它是相同的(/usr/lib/x86_64-linux-gnu/libstdc++.so.6)。我编辑了这个问题以删除clang部分。

奇怪的问题..如果你交换包括cmath和math.h的字符串怎么办?至于我,我避免使用像cmath这样的c *标题

@AntonMalyshev你问的包含顺序?这没什么区别。

您使用的是什么版本的编译器?我无法用col ++重现g ++或clang。

@AntonMalyshev无法避免使用c *标头 - 这是旧代码,需要构建为使用更新标准构建的更大代码库的一部分

@NathanOliver我正在使用g++ 5.4.0。 glibc版本是2.23

哼。显然coliru使用ldd(Ubuntu EGLIBC 2.15-0ubuntu10.13)2.15

@NathanOliver关注你的评论我试过Wandbox。它没有gcc-5.4.0但它确实有5.3.0和6.1.0,我也无法复制......非常奇怪!

不要同时使用cmath和math.h ...

看起来它可能是图书馆的一个错误。 gcc.godbolt.org在4.9.2到5.3之间失败但在6.1上工作

@Ven,我不是。有问题的代码使用math.h。但是,我假设其他一些包含的内容包括最终包含cmath的内容,因此两者都在范围内。因此编译失败,因此我编写这个小应用程序来诊断正在发生的事情

@NathanOliver非常有趣! gcc.godbolt.org无法编译5.3,而wandbox适用于5.3。

非常离奇。看看这种不一致的原因是多么有趣。对不起,我只能混淆问题:)

@NathanOliver不,谢谢你帮忙确认我不是疯了!我会看看bugzilla,看看能不能找到任何东西

@NathanOliver我在Centos gcc-4.9.1和gcc-5.2.1上运行它,它适用于两者。在Ubuntu gcc-4.9.3上运行它,但gcc-5.4.0没有。鉴于godbolt对某些人失败了,但是wandbox没有...好吧,我不知道该怎么想?!

@SteveLorimer如果你在几天内没有找到任何东西给我一个ping,我会给你一个赏金。我真的很想知道发生了什么。

简要总结相关要点,主要来自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模式下:

glibc <  2.23, GCC <  6: uses the macro; uses obsolete signature

glibc >= 2.23, GCC <  6: uses the macro; results in error

glibc <  2.23, GCC >= 6: and use obsolete signature

glibc >= 2.23, GCC >= 6: and use standard signature

@NathanOliver你看到了这个答案吗?

谢谢 - 非常翔实!

@SteveLorimer我现在有。 谢谢T.C. 另一个很好的答案。

如果从GCC查看,它有:

. . .

#include

. . .

#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包含以下代码:

...

#undef isinf

#undef isnan

#undef isnormal

...

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

我可以在godbolt上编译gcc-6.1上的std::isnan和isnan

如果它与undef有问题,那么包含的顺序就很重要,但这并不重要

@AntonMalyshev:为什么订单很重要? cmath几乎总是包含math.h,它将有一个标题保护 - 它将始终包含一次,并且只包含一次。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值