阅读所有答案以及编译器文档后,我决定遵循以下标准。
对于所有文件,无论是项目标头还是外部标头,始终使用以下模式:
#include <namespace/header.hpp>
命名空间至少有一个目录深,以避免冲突。
当然,这意味着项目头所在的项目目录也应该作为“默认包含头”添加到 makefile 中。
之所以做出这样的选择,是因为我查到了以下信息:
1. include "" 模式依赖于编译器
下面我将给出答案
1.a 标准
Source:
- C++14 工作草案 n3797:https://isocpp.org/files/papers/N3797.pdf https://isocpp.org/files/papers/N3797.pdf
- C++11、C++98、C99、C89(所有这些标准中引用的部分均未更改)
在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++:
Source:
- http://msdn.microsoft.com/en-us/library/36k2cdd4.aspx http://msdn.microsoft.com/en-us/library/36k2cdd4.aspx
#include“MyFile.hpp”
预处理器按以下顺序搜索包含文件:
- 与包含 #include 语句的文件位于同一目录中。
- 在任何先前打开的目录中,包含文件的顺序与打开文件的顺序相反。搜索从最后打开的包含文件的目录开始,一直到最先打开的包含文件的目录。
- 沿着每个 /I 编译器选项指定的路径。
- (*) 沿着 INCLUDE 环境变量或开发环境默认包含指定的路径。
#include
预处理器按以下顺序搜索包含文件:
- 沿着每个 /I 编译器选项指定的路径。
- (*) 沿着 INCLUDE 环境变量或开发环境默认包含指定的路径。
注意最后一步
该文档不清楚“沿着 INCLUDE 环境变量指定的路径”部分<...>
and "..."
包括。以下引用使其符合标准:
对于指定为 #include "path-spec" 的包含文件,目录搜索从父文件的目录开始,然后继续搜索任何祖父文件的目录。也就是说,搜索相对于包含源文件的目录开始,该源文件包含正在处理的#include 指令。如果没有祖父母文件并且尚未找到该文件,则搜索将继续,就好像文件名括在尖括号中一样。
因此,最后一步(用星号标记)是阅读整个文档的解释。
1.c g++
Source:
- https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
- https://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html https://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
- https://gcc.gnu.org/onlinedocs/cpp/Include-Operation.html https://gcc.gnu.org/onlinedocs/cpp/Include-Operation.html
- https://gcc.gnu.org/onlinedocs/cpp/Initation.html https://gcc.gnu.org/onlinedocs/cpp/Invocation.html
- https://gcc.gnu.org/onlinedocs/cpp/Search-Path.html https://gcc.gnu.org/onlinedocs/cpp/Search-Path.html
- https://gcc.gnu.org/onlinedocs/cpp/Once-Only-Headers.html https://gcc.gnu.org/onlinedocs/cpp/Once-Only-Headers.html
- https://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html https://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html
- https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
以下引用总结了该过程:
GCC [...] 将查找使用 #include 请求的标头<file>
在 [系统目录] [...] 在默认目录之前按从左到右的顺序搜索所有由 -I 命名的目录
GCC 首先在包含当前文件的目录中查找使用 #include "file" 请求的标头,然后在 -iquote 选项指定的目录中查找,然后在相同的位置查找使用尖括号请求的标头。
#include“MyFile.hpp”
此变体用于您自己的程序的头文件。预处理器按以下顺序搜索包含文件:
- 与包含 #include 语句的文件位于同一目录中。
- 沿着每个 -iquote 编译器选项指定的路径。
- 至于#include
<MyFile.hpp>
#include
此变体用于系统头文件。预处理器按以下顺序搜索包含文件:
- 沿着每个 -I 编译器选项指定的路径。
- 在系统目录内。
1.d Oracle/Sun Studio CC
Source:
- http://docs.oracle.com/cd/E19205-01/819-5265/bjadq/index.html http://docs.oracle.com/cd/E19205-01/819-5265/bjadq/index.html
请注意,文本有些自相矛盾(请参阅示例以理解)。关键短语是:“区别在于,仅搜索当前目录中名称已用引号引起来的头文件。”
#include“MyFile.hpp”
此变体用于您自己的程序的头文件。预处理器按以下顺序搜索包含文件:
- 当前目录(即包含“include”文件的目录)
- 使用 -I 选项命名的目录(如果有)
- 系统目录(例如/usr/include目录)
#include
此变体用于系统头文件。预处理器按以下顺序搜索包含文件:
- 使用 -I 选项命名的目录(如果有)
- 系统目录(例如/usr/include目录)
1.e XL C/C++ 编译器参考 - IBM/AIX
Source:
- http://www.bluefern.canterbury.ac.nz/ucsc%20userdocs/forucscwebsite/c/aix/compiler.pdf http://www.bluefern.canterbury.ac.nz/ucsc%20userdocs/forucscwebsite/c/aix/compiler.pdf
两个文档的标题都是“XL C/C++ 编译器参考” 第一个文档较旧 (8.0),但更容易理解。第二个是较新的(12.1),但解密有点困难。
#include“MyFile.hpp”
此变体用于您自己的程序的头文件。预处理器按以下顺序搜索包含文件:
- 当前目录(即包含“include”文件的目录)
- 使用 -I 选项命名的目录(如果有)
- 系统目录(例如 /usr/vac[cpp]/include 或 /usr/include 目录)
#include
此变体用于系统头文件。预处理器按以下顺序搜索包含文件:
- 使用 -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>
or
#include <GlobalInclude/Header.hpp>
你会注意到,虽然
#include "Header.hpp"
会成功编译,因此仍然隐藏问题,而
#include <Header.hpp>
在正常情况下不会编译。
因此,坚持使用 表示法将强制开发人员在包含文件前加上正确的目录前缀,这是更喜欢 而不是 "" 的另一个原因。
三、结论
同时使用 表示法和命名空间表示法可以消除预编译器猜测文件的可能性,而不是仅搜索默认包含目录。
当然,标准库还是照常包含在内,即:
#include <cstdlib>
#include <vector>