【问题标题】:Is there any way to know which headers are automatically included in C++有什么方法可以知道哪些头文件自动包含在 C++ 中
【发布时间】:2017-05-04 00:10:19
【问题描述】:

这是this 的后续问题

在 C++ 中,与 C 不同,标准头文件允许#include 其他标准头文件。

有什么方法可以知道自动包含了哪些标头,因为可能很难猜测哪些标头中定义了哪些符号。

动机:我的作业在我的计算机上编译并正常工作,但 TA 告诉我它没有编译,需要几个头文件(互斥体和算法)来编译。我如何确定我将来提交的代码是防弹的。

我的编译器没有给出任何关于隐式声明的警告。 我正在使用clang++ -std=c++11 来编译我的代码。

【问题讨论】:

  • 打开你包含的头文件?为什么它仍然很重要?
  • 永远不要依赖这种隐含的头文件,这是一个实现细节,可以随时更改。始终明确包含您需要的功能。
  • @pfannkuchen_gesicht 我提交了一个在我的电脑上完美运行的作业,但是助教在编译它时遇到了问题。所以我想知道有没有办法知道自动包含哪些标头,以便我可以对我的代码进行防弹。
  • @Anarug:啊,将动机放在问题中会改善你的问题——或者甚至将问题改写成你真正想知道的:如何确保你拥有所有@ 987654323@s 你需要是可移植的,而不是隐式包含一些标题。
  • @Raphael:“我需要为这件事包含什么”的问题与“我已经编写了数千行代码;我如何检查我是否明确包含一切都需要吗?”

标签: c++ clang


【解决方案1】:

这个问题与 Are there tools that help organizing #includes? 有很大的重叠,而后者现在被认为是“离题”,因为它要求外部工具/资源。严格来说,这个问题只是关于寻找间接包含的方法,但目标肯定是一样的。


使用“正确”include 语句的问题非常困难。问题中描述的主要是关于间接包含:一个标头包含某个符号,但在另一台具有另一个编译器和另一个标头的机器上,可能不包含该符号。

一些 cmets 和其他答案提出了看似务实但不切实际的方法:

  • 用另一个编译器试试:这很脆弱,并没有告诉你任何关于可能的第三个编译器的信息
  • 阅读包含的标头,查看它们还包含哪些其他标头:。标头的想法正好相反,即不必阅读它们,而只是使用它们提供的 API
  • 生成预处理器输出并编写一个半自动解决问题的工具:不。无论如何,这不会解决不同标头版本的问题。
  • 明确地包含您正在使用的东西所需的标头:这基本上是不可能维护的。在重构期间将一个函数从文件移动到另一个函数时,您永远不知道可以安全地删除一个文件中的哪些包含,以及必须将哪些添加到另一个文件中。

    (当您更改包含时,您可能会破坏包含您的标头的第三方代码,因为每个人都面临同样的问题......)

所有这一切都没有涵盖间接包含并不总是一个问题,但有时是有意的:当你想使用std::vector时,你会#include <vector>,而不是@987654331 @,即使后者包含定义...


因此,唯一现实的解决方案是依靠支持开发人员的工具。 Are there tools that help organizing #includes? 的回答中提到了一些工具,最初,我在三年前的 comment to the question 中提到了 CDT,但我认为这里也值得一提:

Eclipse CDT (C/C++ Development Tooling)

这是一个提供“组织包含”功能的 IDE。这些功能在https://www.eclipse.org/community/eclipse_newsletter/2013/october/article3.php 的文章中进行了描述,其中指出了困难和注意事项(我在上面提到了其中的一些),以及它们在 CDT 中是如何解决的。

我已经尝试过了(很久以前),虽然只是在一个非常简单的测试项目中。所以我可以说它基本上是有效的,但考虑到任务的复杂性,这带有一个免责声明:它接近魔术,但可能仍然存在失败的情况。

C++ 不打算被解析和编译。这句话是正确的,因为我之前关于堆栈溢出的答案之一包含#define not certainly 这一行。

文章还指向另一个工具:

Include What You Use

没有尝试过这个,但它似乎得到了积极的维护。 documentation pages 还列出了一些困难和目标。乍一看,它似乎不像 CDT 那样强大和可配置(当然,它也不包含在 IDE 中),但有些人可能想尝试一下。

【讨论】:

    【解决方案2】:

    如果您想知道特定头文件提取的其他头文件,最简单的方法是仅通过编译器的预处理器阶段运行包含文件,而不是完全编译它。例如,如果您想知道<iostream> 提取了什么,请创建一个仅包含以下内容的文件:

    #include <iostream>
    

    然后对其进行预处理。使用gcc-E 选项仅运行预处理器,不编译文件,并将预处理文件转储到标准输出。结果输出以:

    开头
    # 1 "t.C"
    

    这是我的单行源文件。

    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    

    显然,gcc 无论如何都会自动拉入这个头文件。这个可以忽略。

    # 1 "<command-line>" 2
    # 1 "t.C"
    # 1 "/usr/include/c++/6.2.1/iostream" 1 3
    

    好的,现在我们终于在我的单行源文件中找到了实际的#include 语句。这就是我的&lt;iostream&gt; 所在的位置:

    # 36 "/usr/include/c++/6.2.1/iostream" 3
    
    # 37 "/usr/include/c++/6.2.1/iostream" 3
    
    # 1 "/usr/include/c++/6.2.1/x86_64-redhat-linux/bits/c++config.h" 1 3
    

    好的,所以iostream 本身#includes 这个“c++-config.h”头文件,显然是一个内部编译器头文件。

    如果我继续下去,我可以看到 &lt;iostream&gt; 拉进来,不出所料,&lt;ios&gt;&lt;type_traits&gt; 以及像 stdio.h 这样的 C 头文件。

    编写一个快速的小脚本应该不会太难,它需要一个头文件,在预处理阶段运行编译器,并生成一个漂亮的、格式化的所有头文件列表。

    【讨论】:

    • 我尝试传递 -E 标志,你是对的,它提供了有关所有标题的信息。但是它会打印很多噪音,这很容易让新手感到困惑(包括 4 种不同类型的互斥锁)。
    • 但不能保证结果与使用不同标准库的人相同,对吧?这是问题的上下文。
    【解决方案3】:

    据我所知,没有办法做你想做的事。

    如果您尝试在多个示例平台上编译您的代码,并且成功,那么它更有可能在任何其他平台上编译,但没有简单的方法可以确定。

    根据我的经验,MinGW C++ 标头彼此使用较少的#includes。所以MinGW可以成为检查可移植性的实用工具。

    【讨论】:

      【解决方案4】:

      标准列出了每个标题可用的符号。除此之外,没有任何保证,既不声明明显使用的符号,也不声明并非所有符号。您需要包含 正在使用的任何名称的每个标题。您应该依赖间接包含。

      从积极的方面来说,标准库中没有任何标准库头文件需要额外头文件的情况。

      【讨论】:

      • 我是一个 C++ 菜鸟,我天真地认为互斥锁是在线程头中定义的,现在我的编译器自动包含 并且我认为我所有的包含就足够了。所以唯一的办法是知道我已经包含了所有的头文件是通过文档看看它们是在哪里定义的,这是正确的吗?
      • @AnuragPeshne:是的,我担心这是正确的。我曾尝试创建仅使强制名称可用的标题,但这并不像我希望的那样微不足道。我不知道有任何其他尝试这样做。
      • 很明显,不应依赖间接包含,我认为这不是 OP 所要求的。问题恰恰相反,即如何检测应该明确包含的间接包含。这对于确保代码的可移植性很重要。如果程序员忘记了包含,代码可能仍然在一台机器上编译(由于间接包含),但在另一台机器上会失败。识别和纠正这种情况可能很困难,尤其是在大型代码中。
      猜你喜欢
      • 2018-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-26
      • 2012-11-18
      • 2017-06-12
      相关资源
      最近更新 更多