标准C++函数ABS()在不同C++编译器中的异常行为

标准C++函数ABS()在不同C++编译器中的异常行为,c++,language-lawyer,libstdc++,c++-standard-library,libc++,C++,Language Lawyer,Libstdc++,C++ Standard Library,Libc++,考虑以下计划: #include <cstdio> #include <cmath> int main() { int d = (int)(abs(0.6) + 0.5); printf("%d", d); return 0; } clang++5.0.0输出1见现场演示 clang++3.6.0见现场演示 Microsoft VC++19.00.23506输出1见现场演示 这个节目到底发生了什么?为什么在不同C++编译器上编译时会产生不同的输

考虑以下计划:

#include <cstdio>
#include <cmath>

int main()
{
    int d = (int)(abs(0.6) + 0.5);
    printf("%d", d);
    return 0;
}
clang++5.0.0输出1见现场演示

clang++3.6.0见现场演示

Microsoft VC++19.00.23506输出1见现场演示

这个节目到底发生了什么?为什么在不同C++编译器上编译时会产生不同的输出?为什么程序在同一编译器的不同版本上表现出不同的行为?这是编译器问题还是标准库libstdc++&libc++问题?关于这个C++标准是什么?

我知道我需要写std::abs而不是abs。但这不是我的问题。

所有从C标准库引入功能的cname库头都必须在名称空间std中引入这些符号。它们也可以,但绝对不必将它们引入全局名称空间:

除非第[library]条至第[thread]条和附件中另有说明 [depr],每个标题cname的内容与 C标准库中指定的相应标头名称.h。在里面 但是,C++标准库中除了名称之外的声明 在C中定义为宏的在 名称空间标准。未指定这些名称是否包含 通过[thread]和在子句[language.support]中添加的重载 附录[depr]首先在全局命名空间范围内声明,并且 然后通过使用显式声明将其注入命名空间std

因此,不同的编译器,甚至不同的编译器版本,意味着不同的实现细节。

所有从C标准库引入功能的cname库头都必须在名称空间std中引入这些符号。它们也可以,但绝对不必将它们引入全局名称空间:

除非第[library]条至第[thread]条和附件中另有说明 [depr],每个标题cname的内容与 C标准库中指定的相应标头名称.h。在里面 但是,C++标准库中除了名称之外的声明 在C中定义为宏的在 名称空间标准。未指定这些名称是否包含 通过[thread]和在子句[language.support]中添加的重载 附录[depr]首先在全局命名空间范围内声明,并且 然后通过使用显式声明将其注入命名空间std


因此,不同的编译器,甚至不同的编译器版本,意味着不同的实现细节。

因此,如果编译器和库升级,程序的行为很可能会发生变化&它仍将被视为标准。是吗?@析构函数-是的。获得保证行为的唯一方法是通过命名空间STD.进行更深入的查看,考虑ABS本质上是在C和C++之间进行分割的。还可以考虑从问题中包含的两个头中包含的一个,这可以很容易地改变每个实现和版本。abs是在关于更改GCC对C头的处理的文章中描述的事情之一。@chris:如果您编写answer@Destructor,这样的答案对于问题中每一个案例的确切情况都是非常有用的,但我没有这些信息。我只能根据输出推测,有些实现选择整数版本,导致输出为0,有些选择双精度版本,导致输出为1。我很难说清楚为什么在每种情况下都会选择其中一种而不花很多时间,特别是对于全局名称是否可用于C++版本的实现定义。因此,如果编译器和库升级,程序的行为可能会改变,并且仍然将被认为是标准确认。是吗?@析构函数-是的。获得保证行为的唯一方法是通过命名空间STD.进行更深入的查看,考虑ABS本质上是在C和C++之间进行分割的。还可以考虑从问题中包含的两个头中包含的一个,这可以很容易地改变每个实现和版本。abs是在关于更改GCC对C头的处理的文章中描述的事情之一。@chris:如果您编写answer@Destructor,这样的答案对于问题中每一个案例的确切情况都是非常有用的,但我没有这些信息。我只能根据输出推测,有些实现选择整数版本,导致输出为0,有些选择双精度版本,导致输出为1。我很难确切地说出为什么在每种情况下都不需要花费大量的时间来选择,尤其是在全局名称是否可用于C++版本的实现定义上。g++实际上是
这一次,我们用最不标准的方法代替VS。数学标题很不统一。g++实际上是最非标准的,而不是VS。
prog.cc: In function 'int main()':
prog.cc:6:26: error: 'abs' was not declared in this scope
     int d = (int)(abs(0.6) + 0.5);
                          ^
prog.cc:6:26: note: suggested alternative:
In file included from prog.cc:2:0:
/opt/wandbox/gcc-6.3.0/include/c++/6.3.0/cmath:103:5: note:   'std::abs'
     abs(_Tp __x)
     ^~~
prog.cc:6:19: error: use of undeclared identifier 'abs'; did you mean 'fabs'?
    int d = (int)(abs(0.6) + 0.5);
                  ^~~
                  fabs
/usr/include/x86_64-linux-gnu/bits/mathcalls.h:181:14: note: 'fabs' declared here
__MATHCALLX (fabs,, (_Mdouble_ __x), (__const__));
             ^
/usr/include/math.h:71:26: note: expanded from macro '__MATHCALLX'
  __MATHDECLX (_Mdouble_,function,suffix, args, attrib)
                         ^
/usr/include/math.h:73:22: note: expanded from macro '__MATHDECLX'
  __MATHDECL_1(type, function,suffix, args) __attribute__ (attrib); \
                     ^
/usr/include/math.h:76:31: note: expanded from macro '__MATHDECL_1'
  extern type __MATH_PRECNAME(function,suffix) args __THROW
                              ^
/usr/include/math.h:79:42: note: expanded from macro '__MATH_PRECNAME'
#define __MATH_PRECNAME(name,r) __CONCAT(name,r)
                                         ^
/usr/include/x86_64-linux-gnu/sys/cdefs.h:88:23: note: expanded from macro '__CONCAT'
#define __CONCAT(x,y)   x ## y
                        ^
1 error generated.