【发布时间】:2016-08-30 12:03:00
【问题描述】:
C++ 标准在 <cmath> 标头中定义了一些重载函数,这些函数不属于 C 中的 <math.h> 标头(因为 C 没有重载)。
其中有float abs(float)、double abs(double)、long double abs(long double) 和double abs(Integral)。
另一方面,abs 根本没有在 C <math.h> 中定义(而是在 <stdlib.h> 中),唯一的签名是 int abs(int)。
现在在我的系统上,当使用带有 C++ 程序的 C++ 编译器时,#include <math.h> 不会在全局命名空间或 std 中提供 C++ abs 重载。
另一方面,#include <cmath> 定义了std::abs。
这正是我所期望的——包含 C 版本以获取 C 函数,并包含 C++ 版本以获取 C++ 函数。 @visitor 的This answer 提到了同样的事情。
但是,用户 @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;
}
如果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';
libstdc++ docs on the subject
建议包含 C++ 风格的标头 <c*> 而不是 C 风格的标头 <*.h> 正是因为
C++ 风格的头文件使用函数重载,而 C 风格的头文件没有。
苹果 libc++
$ clang++ -Wall -Wextra abs2.cc -o abs2
abs2.cc:4:5: error: use of undeclared identifier 'abs'; did you mean 'fabs'?
此外,如果您还包括<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);
^~~ ~~~
这明确表示浮点重载只能从<cmath> 获得,而不是从
C 传统头文件。
Apache libstdcxx
我没有安装它,但是检查了math.h header,它将<cmath> 中同样定义在C 的<math.h> 中的那些函数带入了全局命名空间,但不包括abs。
OpenWatcom C++
再次检查cmath/math.h header,
当用作 math.h 时,它会将与 Apache libstdcxx 相同的功能带入全局命名空间,但不包括 abs。
STL端口
检查math.h header,它包括系统的C <math.h> 标头,它不是C++ 库的一部分,因此不包括abs。 (这也是g++和clang++所做的。)
Microsoft Visual Studio (Dinkumware)
我自己没有访问权限, 但this site 声称使用 Visual C++ 进行编译, 它说
error C4578: 'abs': conversion from 'double' to 'int', possible loss of data
(Did you mean to call 'fabs' or to #include <cmath>?)
所以,实际上是每个主要的 C++ 标准库实现 这点违反标准?
或者我们是否遗漏了标准中关于 <math.h> 和其他传统 C 标头的内容?
【问题讨论】:
-
来自@JonathanWakely 的有趣且相关的阅读:developers.redhat.com/blog/2016/02/29/…
-
@myaut 确实,非常相关!听起来 GCC 6 应该是
#include <math.h>引入abs重载的第一个平台。我会看看。老实说,我更喜欢<cxxx>带来 C++ 风格的重载,<xxx.h>直接包含系统 C 标头,如果您愿意的话。
标签: c++ c++-standard-library math.h