标准库函数 abs() 在不同 C++ 编译器上的异常行为

Unusual behavior of standard library function abs() on different C++ compilers

考虑以下程序:

#include <cstdio>
#include <cmath>

int main()
{
    int d = (int)(abs(0.6) + 0.5);
    printf("%d", d);
    return 0;
}

g++ 7.2.0 输出 0(查看现场演示 here

g++ 6.3.0(查看现场演示 here

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)
     ^~~

clang++ 5.0.0 输出 1(查看现场演示 here

clang++ 3.6.0(查看现场演示 here

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.

Microsoft VC++ 19.00.23506 输出 1(查看现场演示 here

这个程序到底发生了什么?为什么在不同的 C++ 编译器上编译时会给出不同的输出?为什么即使在同一编译器的不同版本上,程序也会表现出不同的行为?这是编译器问题还是标准库 (libstdc++ & libc++) 问题? C++ 标准对此有何规定?

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

所有从 C 标准库引入功能的 cname 库头文件必须在命名空间 std 中引入这些符号。它们也可以,但绝对不是必须将它们引入全局命名空间。 [headers]/4:

Except as noted in Clauses [library] through [thread] and Annex [depr], the contents of each header cname is the same as that of the corresponding header name.h as specified in the C standard library. In the C++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope of the namespace std. It is unspecified whether these names (including any overloads added in Clauses [language.support] through [thread] and Annex [depr]) are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations.

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