C++标准是否需要`#include <math.h>`来定义`<cmath>`中的`abs`重载?

Nic*_*teo 15 c++ math.h c++-standard-library

C++标准在<cmath>头文件中定义了一些不属于<math.h>C中头部的重载函数(因为C没有重载).其中有float abs(float),double abs(double),long double abs(long double),和double abs(Integral).另一方面,absC <math.h>中根本没有定义(<stdlib.h>相反),唯一的签名是int abs(int).

现在在我的系统上,当使用带有C++程序的C++编译器时,#include <math.h>不会abs在全局命名空间或中提供C++ 重载std.另一方面,#include <cmath>定义std::abs.

这是我所期望的 - 包括C版本以获取C函数,并包含C++版本以获得C++函数.@visitor的回答提到了同样的事情.

但是,用户@ Cheers-and-hth-Alf坚持认为这违反了标准,因为它说"每个C头,每个都有一个表单的名称name.h,就像每个名称放在标准库名称空间中一样通过相应的cname头放在全局命名空间范围内." (本节D.5.2在C++ 03,C++ 11和C++ 14之间似乎没有太大变化.)

检查你的平台做什么很容易:看看会发生什么

#include <math.h>

int main() {
    abs(1.2);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果abs未声明,<math.h>则不包括C++函数.

如果它编译,然后尝试包括<stdio.h> 并添加printf("%g\n", abs(1.2)); 如果这抱怨格式不匹配,或打印1,则<math.h> 包括C int abs(int)函数(通常在<stdlib.h>).(最好避免<iostream>和其他C++标题一起使用,因为它们往往会引入<cstdlib>并混淆问题.)

这是我发现的:

GNU libstdc ++

$ g++ -Wall -Wextra abs2.cc -o abs2
abs2.cc: In function 'int main()':
abs2.cc:5:22: error: 'abs' was not declared in this scope
  std::cout << abs(1.2) << '\n';
Run Code Online (Sandbox Code Playgroud)

关于该主题libstdc ++文档 建议包括C++风格的头文件<c*>而不是C风格的头文件,<*.h>因为C++风格的头文件使用函数重载,而C风格的头文件则不然.

Apple libc ++

$ clang++ -Wall -Wextra abs2.cc -o abs2
abs2.cc:4:5: error: use of undeclared identifier 'abs'; did you mean 'fabs'?
Run Code Online (Sandbox Code Playgroud)

此外,如果您还要包含<stdlib.h>以获取定义abs,则clang ++会提供更有用的错误消息

abs2.cc:5:5: warning: using integer absolute value function 'abs' when argument is of floating point type [-Wabsolute-value]
    abs(1.2);
    ^
abs2.cc:5:5: note: use function 'std::abs' instead
    abs(1.2);
    ^~~
    std::abs
abs2.cc:5:5: note: include the header <cmath> or explicitly provide a declaration for 'std::abs'
abs2.cc:5:9: warning: implicit conversion from 'double' to 'int' changes value from 1.2 to 1 [-Wliteral-conversion]
    abs(1.2);
    ~~~ ^~~
abs2.cc:5:5: warning: ignoring return value of function declared with const attribute [-Wunused-value]
    abs(1.2);
    ^~~ ~~~
Run Code Online (Sandbox Code Playgroud)

这明确地说浮动重载只能<cmath>从C传统头文件中获得,而不是从传统的头文件中获取.

Apache libstdcxx

我没有安装它,但检查math.h标头,它将那些<cmath>也在C中定义的函数<math.h>带入全局命名空间,但不包括abs.

OpenWatcom C++

再次,检查cmath/math.h标头,当使用math.h它时,它将相同的函数带入Apache libstdcxx所做的全局命名空间,不包括abs.

STLPort的

检查math.h标头,它包含系统的C <math.h>头,它不是C++库的一部分,因此不包括abs.(这也是g ++和clang ++所做的.)

Microsoft Visual Studio(Dinkumware)

我自己无权访问,但该网站声称使用Visual C++进行编译,它说

error C4578: 'abs': conversion from 'double' to 'int', possible loss of data
(Did you mean to call 'fabs' or to #include <cmath>?) 
Run Code Online (Sandbox Code Playgroud)

那么,字面意思是每个主要的C++标准库实现都违反了这一点上的标准吗?

或者我们是否遗漏了标准所说的内容<math.h>和其他传统的C标题?

Gal*_*lik 2

“每个 C 头文件都有一个 name.h 形式的名称,其行为就好像由相应的 cname 头文件放置在标准库名称空间中的每个名称都放置在全局名称空间范围内一样。”

对我来说,这种措辞并不是说<cname>标题中的每个名称都必须出现在name.h标题中。

它表示标头中具有对应名称的每个名称都必须<name.h>出现<cname>全局命名空间中。

它没有提及<cname>标题中未出现在<name.h>标题中的名称。

C++14标准

D.5 C 标准库头文件[ depr.c.headers ]

为了与C标准库和C Unicode TR兼容,C++标准库提供了26个C头文件,如表

该声明使用术语“26 个 C 标头”,表明它们包含 C 标准规定应包含的内容,而不是标准规定C++<cname>包含的内容。

事实上,<cstddef>例如,它详细说明了相应标头中未包含的C内容。

例如

18.2 类型 [支持.类型]

2内容与标准C库头相同<stddef.h>,但有以下变化...