【问题标题】:Does <algorithm> include <cmath>?<algorithm> 是否包含 <cmath>?
【发布时间】:2015-04-05 05:41:35
【问题描述】:

以下程序编译正确:

#include <algorithm>

int main(int argc, char *argv[]) {

    return int(log(23.f));

}

(在 g++ 4.9.2 下,带有标志 -std=c++11

代码使用函数log,该函数在&lt;cmath&gt; 上定义。但是,它不包括标头&lt;cmath&gt;,仅包括标头&lt;algorithm&gt;。为什么g++ 没有给出任何警告,并且编译正确?

【问题讨论】:

  • 任何 C++ 标头都可以包含任何其他标头,但并非必须如此。在您的系统上,&lt;algorithm&gt; 似乎需要&lt;cmath&gt;。这很好,但不要依赖它。其他系统可能不会这样做,并且您的代码不会在其他系统上编译。并且那些其他系统上的编译器完全正确,不编译你的编译器同样正确的编译。 IWYU:包括您使用的内容。
  • 顺便说一句 cmath 定义 std::loglog 可能来自 cmath 实现,包括 math.h
  • ... 或其他内容,包括 &lt;math.h&gt;,即使没有引入 &lt;cmath&gt;。我不确定这是否符合要求,但这是现状。在全局命名空间中定义标准 C 库名称,后果自负。
  • @Potatoswatter 包括 (根据 [res.on.headers]/3)。
  • @fonini,只是为了添加一些细节,在 C++11 模式下,GCC 的 &lt;algorithm&gt; 包含 &lt;random&gt;,因为 std::shuffle() 使用 std::uniform_int_distribution,而 &lt;random&gt; 包含许多标题,包括&lt;cmath&gt;.

标签: c++ c++11 standards c++-standard-library


【解决方案1】:

根据标准,某些标题确实包含其他标题。例如,&lt;cinttypes&gt; 包括 &lt;cstdint&gt;。请参阅包含部分here。关于&lt;algorithm&gt;,没有关于它应该包括哪些其他标题的声明(参见here)。因此,结论是,&lt;algorithm&gt; 不需要包含&lt;cmath&gt;,并且您的示例代码不可移植。它可能无法在其他 C++ 实现上编译。

【讨论】:

    【解决方案2】:

    在 C++11 标准中,[res.on.headers]/1 声明

    一个 C++ 头文件可能包含其他 C++ 头文件。 C++ 标头应提供 出现在其概要中的声明和定义。 一个 C++ 在其概要中显示的标头包括其他 C++ 标头应 提供出现在概要中的声明和定义 其他标题。

    现在考虑 [algorithms.general]/2:

    标题&lt;algorithm&gt;概要

    #include <initializer_list>
    
    namespace std {
      // ......
    

    &lt;cmath&gt; 未列出,并且显然未包含在 &lt;initializer_list&gt; 中。因此,不能保证您的程序在符合标准的实现上进行编译。永远不应该依赖“隐式包含” - 一般准则是包含使用实体的每个标头。
    例外是例如&lt;iostream&gt; 包括 &lt;ostream&gt;,从 C++11 开始保证。

    【讨论】:

      【解决方案3】:

      回答你的问题:

      为什么g++没有给出任何警告,并且编译正确?

      因为不需要 C++ 实现,而且考虑到 #include 的工作方式,实现此警告实际上非常困难。 Attempts 已经提出,但还有一些问题没有完全解决。


      移动到different model 可以启用这种检查。然而,为了向后兼容并允许最简单的转换,我使用的标准库的“模块化”恰好明确允许以前依赖于间接包含的代码继续工作。

      例如,您可以在 libc++ 的module map 中看到这一点;那些export * 行声明“此模块导入的任何模块也将被导出”。也就是说,导入模块 std.cmath 的模块 std.algorithm 也会导出,因此任何导入 std.algorithm 的人也可以访问 std.cmath。

      对于新代码,如果可以关闭这些“遗留导出”会非常好,但对于预先存在的大型项目,只需打开 -fmodules 并让项目在没有任何更改的情况下运行是非常好的.


      使用 clang 的 libc++ 模块实现,并修改模块映射文件以删除不可移植的间接包含行为,clang 报告如下错误:

      main.cpp:5:16: 错误:必须先从模块“Darwin.C.math”导入“log”声明

      return int(log(23.f));
                 ^  
      

      /usr/include/math.h:387:15: 注意:之前的声明在这里

      extern double log(double);  
                    ^  
      

      生成 1 个错误。

      libc++ &lt;algorithm&gt; 不包含&lt;cmath&gt;,所以我改用&lt;random&gt;。否则产生上述内容的来源与您显示的相同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-21
        • 1970-01-01
        • 2019-05-04
        • 2014-04-25
        相关资源
        最近更新 更多