是否<algorithm>包括 <cmath>?

Does <algorithm> include <cmath>?

本文关键字:gt lt cmath algorithm 是否 包括      更新时间:2023-10-16

以下程序编译正确:

#include <algorithm>
int main(int argc, char *argv[]) {
    return int(log(23.f));
}

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

该代码使用在<cmath>上定义的函数log。然而,它不包括报头<cmath>,仅包括报头<algorithm>。为什么g++没有给出任何警告,并且正确编译了代码?

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

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

C++标头可以包括其他C++标头。C++标头应提供其概要中出现的声明和定义AC++其概要中显示的标题(包括其他C++标题)应提供概要中出现的声明和定义其他标头的

现在考虑[算法.通用]/2:

标题<algorithm>简介

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

<cmath>未列出,且明显未包含在<initializer_list>中。因此,您的程序不能保证在符合标准的实现上编译。一个人永远不应该依赖";隐含包含"-一般准则是包括使用实体的每个标头
例外情况例如<iostream>,包括<ostream>,这是从C++11开始保证的。

回答您的问题:

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

因为C++实现不需要,而且考虑到#include的工作方式,实际上很难实现这个警告。虽然已经进行了尝试,但仍有一些问题尚未完全解决。


移动到不同的模型可以实现这种检查。然而,为了向后兼容性和允许最简单的转换,我使用的标准库的"模块化"恰好明确允许以前依赖间接包含的代码继续工作。

例如,您可以在libc++的模块映射中看到这一点;这些export *行声明"该模块导入的任何模块也将导出。"也就是说,导入模块std.cmath的模块std.algorithm也将导出,因此任何导入std.algarithm的人都可以访问std.cmath.

对于新代码,如果可以关闭这些"遗留导出",那将是非常好的,但对于预先存在的大型项目,能够打开-fmodule并使项目在没有更改的情况下工作是非常好。


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

main.cpp:5:16:错误:"log"的声明必须从模块"Darwin.C.math"导入,然后才需要

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

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

extern double log(double);  
              ^  

生成1个错误。

libc++<algorithm>不包括<cmath>,所以我使用了<random>。否则,产生上述内容的来源与您显示的内容相同。