在阅读了所有答案以及编译器文档后,我决定遵循以下标准。
对于所有文件,无论是项目标头还是外部标头,始终使用以下模式:
#include <namespace/header.hpp>
命名空间至少有一个目录深,以避免冲突。
当然,这意味着项目头所在的项目目录也应该作为“默认包含头”添加到makefile中。
之所以选择这个,是因为我找到了以下信息:
1. include "" 模式依赖于编译器
我会在下面给出答案
1.a 标准
资源:
在第 16.2 节源文件包含中,我们可以读到:
形式的预处理指令
#include <h-char-sequence> new-line
在一系列实现定义的位置中搜索由 < 和 > 分隔符之间的指定序列唯一标识的标头,并用标头的全部内容替换该指令。如何指定位置或标识的标头是实现定义的。
这意味着 #include <...> 将以实现定义的方式搜索文件。
然后,下一段:
形式的预处理指令
#include "q-char-sequence" new-line
导致将该指令替换为由 " 分隔符之间的指定序列标识的源文件的全部内容。以实现定义的方式搜索命名的源文件。如果不支持此搜索,或者搜索失败, 该指令被重新处理,就好像它读取
#include <h-char-sequence> new-line
具有与原始指令相同的包含序列(包括 > 字符,如果有的话)。
这意味着 #include "..." 将以实现定义的方式搜索文件,然后,如果未找到该文件,将进行另一次搜索,就好像它是一个 #include <...>
结论是我们必须阅读编译器文档。
请注意,由于某种原因,标准中的“系统”或“库”标头或其他标头之间没有任何区别。唯一的区别似乎是 #include <...> 似乎针对标头,而 #include "..." 似乎针对源(至少,在英文措辞中)。
1.b 视觉 C++:
资源:
#include "我的文件.hpp"
预处理器按以下顺序搜索包含文件:
- 在与包含#include 语句的文件相同的目录中。
- 在任何先前打开的包含文件的目录中,以打开它们的相反顺序。搜索从最后打开的包含文件的目录开始,一直到最先打开的包含文件的目录。
- 沿着每个 /I 编译器选项指定的路径。
- (*) 沿着 INCLUDE 环境变量指定的路径或开发环境默认包含的路径。
#include <我的文件.hpp>
预处理器按以下顺序搜索包含文件:
- 沿着每个 /I 编译器选项指定的路径。
- (*) 沿着 INCLUDE 环境变量指定的路径或开发环境默认包含的路径。
注意最后一步
该文档并不清楚两者的“沿包含环境变量指定的路径”部分<...>
和"..."
包含。以下引用使其符合标准:
对于指定为#include "path-spec" 的包含文件,目录搜索从父文件的目录开始,然后通过任何祖父文件的目录进行。也就是说,搜索相对于包含正在处理的#include 指令的源文件的目录开始。如果没有祖父文件且未找到该文件,则继续搜索,就像文件名包含在尖括号中一样。
因此,最后一步(用星号标记)是阅读整个文档的解释。
1.c g++
资源:
以下引用总结了该过程:
<file>
GCC [...] 将在 [系统目录] [...] 中查找使用 #include 请求的标头
GCC 首先在包含当前文件的目录中查找使用 #include "file" 请求的头文件,然后在 -iquote 选项指定的目录中查找,然后在相同的位置查找使用尖括号请求的头文件。
#include "我的文件.hpp"
此变体用于您自己程序的头文件。预处理器按以下顺序搜索包含文件:
- 在与包含#include 语句的文件相同的目录中。
- 沿着每个 -iquote 编译器选项指定的路径。
- 至于#include
<MyFile.hpp>
#include <我的文件.hpp>
此变体用于系统头文件。预处理器按以下顺序搜索包含文件:
- 沿着每个 -I 编译器选项指定的路径。
- 在系统目录里面。
1.d 甲骨文/Sun Studio CC
资源:
请注意,文本有些自相矛盾(请参阅示例以理解)。关键短语是:“区别在于当前目录只搜索名称用引号括起来的头文件。 ”
#include "我的文件.hpp"
此变体用于您自己程序的头文件。预处理器按以下顺序搜索包含文件:
- 当前目录(即包含“包含”文件的目录)
- 使用 -I 选项命名的目录(如果有)
- 系统目录(例如 /usr/include 目录)
#include <我的文件.hpp>
此变体用于系统头文件。预处理器按以下顺序搜索包含文件:
- 使用 -I 选项命名的目录(如果有)
- 系统目录(例如 /usr/include 目录)
1.e XL C/C++ 编译器参考 - IBM/AIX
资源:
两个文档的标题为“XL C/C++ Compiler Reference” 第一个文档较旧 (8.0),但更易于理解。第二个是较新的(12.1),但更难解密。
#include "我的文件.hpp"
此变体用于您自己程序的头文件。预处理器按以下顺序搜索包含文件:
- 当前目录(即包含“包含”文件的目录)
- 使用 -I 选项命名的目录(如果有)
- 系统目录(例如 /usr/vac[cpp]/include 或 /usr/include 目录)
#include <我的文件.hpp>
此变体用于系统头文件。预处理器按以下顺序搜索包含文件:
- 使用 -I 选项命名的目录(如果有)
- 系统目录(例如 /usr/vac[cpp]/include 或 /usr/include 目录)
1.e 结论
模式 "" 可能会导致编译器之间出现细微的编译错误,并且由于我目前在 Windows Visual C++、Linux g++、Oracle/Solaris CC 和 AIX XL 上工作,因此这是不可接受的。
无论如何,“”描述的功能的优势无论如何都不是有趣的,所以......
2. 使用 {namespace}/header.hpp 模式
我在工作中看到(即这不是理论,这是现实生活中痛苦的职业经历)两个同名的标题,一个在本地项目目录中,另一个在全局包含中。
由于我们使用的是 "" 模式,并且该文件同时包含在本地头文件和全局头文件中,当出现奇怪的错误时,我们无法理解到底发生了什么。
使用包含中的目录可以节省我们的时间,因为用户必须写:
#include <MyLocalProject/Header.hpp>
或者
#include <GlobalInclude/Header.hpp>
你会注意到,虽然
#include "Header.hpp"
会成功编译,因此,仍然隐藏问题,而
#include <Header.hpp>
在正常情况下不会编译。
因此,坚持 <> 符号将强制开发人员使用正确的目录作为包含前缀,这是首选 <> 而不是“”的另一个原因。
三、结论
同时使用 <> 表示法和命名空间表示法从预编译器中消除了猜测文件的可能性,而不是只搜索默认的包含目录。
当然,标准库仍然照常包含,即:
#include <cstdlib>
#include <vector>