【问题标题】:Does the C++ standard require `#include <math.h>` to define the `abs` overloads found in `<cmath>`?C++ 标准是否需要`#include <math.h>` 来定义`<cmath>` 中的`abs` 重载?
【发布时间】:2016-08-30 12:03:00
【问题描述】:

C++ 标准在 &lt;cmath&gt; 标头中定义了一些重载函数,这些函数不属于 C 中的 &lt;math.h&gt; 标头(因为 C 没有重载)。 其中有float abs(float)double abs(double)long double abs(long double)double abs(Integral)。 另一方面,abs 根本没有在 C &lt;math.h&gt; 中定义(而是在 &lt;stdlib.h&gt; 中),唯一的签名是 int abs(int)

现在在我的系统上,当使用带有 C++ 程序的 C++ 编译器时,#include &lt;math.h&gt; 不会在全局命名空间或 std 中提供 C++ abs 重载。 另一方面,#include &lt;cmath&gt; 定义了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 未声明,则&lt;math.h&gt; 不包含C++ 函数。

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

这是我发现的:

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++ 风格的标头 &lt;c*&gt; 而不是 C 风格的标头 &lt;*.h&gt; 正是因为 C++ 风格的头文件使用函数重载,而 C 风格的头文件没有。

苹果 libc++

$ clang++ -Wall -Wextra abs2.cc -o abs2
abs2.cc:4:5: error: use of undeclared identifier 'abs'; did you mean 'fabs'?

此外,如果您还包括&lt;stdlib.h&gt; 来获得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);
    ^~~ ~~~

这明确表示浮点重载只能从&lt;cmath&gt; 获得,而不是从 C 传统头文件。

Apache libstdcxx

我没有安装它,但是检查了math.h header,它将&lt;cmath&gt; 中同样定义在C 的&lt;math.h&gt; 中的那些函数带入了全局命名空间,但不包括abs

OpenWatcom C++

再次检查cmath/math.h header, 当用作 math.h 时,它会将与 Apache libstdcxx 相同的功能带入全局命名空间,但不包括 abs

STL端口

检查math.h header,它包括系统的C &lt;math.h&gt; 标头,它不是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++ 标准库实现 这点违反标准?

或者我们是否遗漏了标准中关于 &lt;math.h&gt; 和其他传统 C 标头的内容?

【问题讨论】:

  • 来自@JonathanWakely 的有趣且相关的阅读:developers.redhat.com/blog/2016/02/29/…
  • @myaut 确实,非常相关!听起来 GCC 6 应该是#include &lt;math.h&gt; 引入abs 重载的第一个平台。我会看看。老实说,我更喜欢 &lt;cxxx&gt; 带来 C++ 风格的重载,&lt;xxx.h&gt; 直接包含系统 C 标头,如果您愿意的话。

标签: c++ c++-standard-library math.h


【解决方案1】:

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

在我看来,这种措辞并不是说&lt;cname&gt; 标头中的每个名称都必须出现在name.h 标头中。

它表示&lt;name.h&gt; 标头中的每个名称&lt;cname&gt; 标头中具有相应名称 必须出现在全局命名空间中。

它没有说明&lt;cname&gt; 标头中没有出现在&lt;name.h&gt; 标头中的名称。

C++14标准

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

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

此语句使用术语“26 个 C 标头”,表明它们包含 C 标准所说的应该包含的内容,而不是 C++ 标准所说的 &lt;cname&gt; 应该包含的内容。

确实,例如,关于&lt;cstddef&gt;,它详细说明了相应C 标头中未包含的内容。

例如

18.2 类型 [ support.types ]

2内容与标准C库头&lt;stddef.h&gt;相同,有如下改动...

【讨论】:

  • 另外,它说的是 name,而不是“过载”。 abs 是一个名字。将abs 放在全局命名空间中(不声明额外的重载)符合此要求
猜你喜欢
  • 2013-03-17
  • 1970-01-01
  • 2023-03-04
  • 2016-11-18
  • 1970-01-01
  • 2016-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多