用 C/C++ 编写库时使用哪种类型的 #include("" 或 <>)

Phi*_*erg 6 c c++ dll include lib

我正在用 C++ 编写一个库。该库有多个头文件和 cpp 文件,需要跨平台(Windows Visual Studio 和 Linux gcc)。构建时,库和头文件安装在某个系统目录中,在同一台机器上的其他代码可以找到它们(例如 Linux 系统上的 /usr/local)。

如果我的标题之一需要#include 我的其他标题之一,那么我应该使用尖括号还是引号?

我觉得安装库后应该使用尖括号,以便检查系统目录,但是在构建库时我需要使用引号,以便检查本地目录并且我不会选择过时的版本从系统目录。

我知道 ow 的不同版本#include <filename>#include “filename”含义。对于编写库的情况,我在问哪个是合适的,为什么是合适的。

Emi*_*ier 11

目前,我正在开发的一个库也面临着同样的决定,该库旨在供我自己以外的项目使用。这里的其他答案都没有解决库本身内包含的引用形式与角度形式的实用性,而只是解释了两种形式之间的技术差异。

似乎存在一种分歧,哪种包含形式最适合库源/标头(包括来自同一库的标头)。我认为我没有资格给出最终答案。相反,我在这里要做的是尝试尽可能地总结每种方法的优缺点,并链接到我在网上找到的一些资源。

C++ 核心指南 [1] 在 SF.12 下有这样的规定:

库创建者应将其标头放入文件夹中,并让客户端使用相对路径包含这些文件#include <some_library/common.h>

下面列出的角度形式的优点假设库的标头被放入以库命名的“根”文件夹中(我讨厌库不这样做)。

不幸的是,C++ 核心指南没有明确说明库创建者在从自己的库源/头文件中包含自己的头文件时应该做什么。然而,一位贡献者在 [4] 中友好地向我澄清,在他们的示例中:

#include "foo_utils/utils.h"
    // A file locally relative to foo.cpp in the same project, use the "" form
Run Code Online (Sandbox Code Playgroud)

“项目”一词也适用于图书馆。该贡献者还澄清说,“局部相对”由读者自行决定,以便#include "../../include/somelib/foo.hpp"可以将 的实例解释为非局部相对,以便#include <somelib/foo.hpp>可以改为使用。

请注意,Stroutrup 共同撰写的 C++ 核心指南在首选形式方面与 Stroutrup 早期 JSF 编码标准 [2] 的 AV 规则 33 有所矛盾#include。也许他对此改变了看法。

该方法的优点#include <somelib/foo.hpp>

  • -I正如 [3] 中所讨论的,它允许用户通过将其放入之前通过或编译器标志搜索过的目录中来提供自己的修补库头文件-isystem。通过此方法进行修补可能仅适用于仅包含头文件的库。
  • 如果您的库的标头之一与系统的标头之一命名相同,那么您所指的是哪个标头就不会那么混乱。例如:#include <somelib/float.h>vs #include "float.h",其中 float.h 恰好是 C 标准库头文件。
  • 避免依赖编译器之间引用形式的不一致行为,并符合 Stroutrup 的 JSF 编码标准 [2] 的 AV 规则 33。如果该库的目标不是跨多个编译器移植,这可能不是问题。
  • 当包含文件位于包含文件的父目录或同级目录中时,它可以避免丑陋的情况。例如#include <somelib/foo/bar.hpp>vs #include "../foo/bar.hpp"。还要考虑源文件和头文件位于库项目的不同目录中的情况:#include <somelib/foo/bar.hpp>#include "../include/foo/bar.hpp"从库 cpp 文件包含时相比。
  • 当在库的 cpp 文件中使用时,它允许用户简单地将库的 cpp 文件嵌入到应用程序的构建系统中,而不必在库的源文件和头文件之间维护相同的相对目录结构。如果库仅包含一个(或几个)cpp 文件,并且应用程序项目不想使用库提供的构建方法,那么这可能是理想的选择。例如,应用程序项目使用 bazel,但该库仅提供 CMake 构建。

该方法的优点#include "foo.hpp"

  • 向读者明确要包含的文件与包含文件属于同一“项目”的一部分。
  • 如果用户同时拥有该库的本地安装版本和系统安装版本,则可以避免在用户忽略设置正确-I-isystem标志的情况下意外包含系统安装的标头。
  • 如果初学者用户不知道如何正确设置-I-isystem标记以指向库的本地版本,他们可以这样做#include "../dependencies/somelib/include/somelib/foo.hpp"(我觉得这非常难看 - 也许是不合理的)。

基于上述研究以及 C++ 核心指南贡献者的帮助,我个人现在的目标是采用以下约定:

  • 当库头文件包含同一库的另一个头文件时:使用#include "foo.h"#include "bar/foo.hpp"#include "../bar/foo.hpp",具体取决于另一个头文件的相对位置。
  • 当库源 (cpp) 文件包含库自己的标头之一时:使用#include <somelib/foo.hpp>#include <somelib/bar/foo.hpp>.

前者明确表示我想要包含一个头文件,该头文件与执行包含操作的文件使用相同的库捆绑在一起。

后者使得库源文件可以通过直接放入应用程序的构建系统来进行编译,而不必在库的源文件和头文件之间维护相同的相对目录结构。它还避免了#include "../include/somelib/foo.hpp"有利于清洁工的丑陋#include <somelib/foo.hpp>

当使用库的 CMake 脚本编译我的库源文件(以生成静态/共享库)时,CMake 脚本控制-I-isystem标志,因此可以确保正确的库头路径将获得最高搜索优先级。


[1] https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rs-incform

[2] https://www.stroustrup.com/JSF-AV-rules.pdf

[3] https://lists.boost.org/Archives/boost//2008/09/142030.php

[4] https://github.com/isocpp/CppCoreGuidelines/pull/1596#issuecomment-1113901271


Riz*_*wan 7

当您使用尖括号时,编译器会在包含路径列表中搜索文件。当您使用双引号时,它首先搜索当前目录(即正在编译的模块所在的目录),然后才会搜索包含路径列表。

因此,按照惯例,您可以使用尖括号表示标准包含,使用双引号表示其他内容。这可以确保在(不推荐)您拥有与标准标头同名的本地标头的情况下,在每种情况下都会选择正确的标头。

请参阅以下 SO 答案以获取更多详细信息

C++ 中包含头文件时尖括号 < > 和双引号 " " 之间的区别?


小智 1

如果标头在您的工作目录中,您应该使用"" ,但是,如果标头在系统路径中或在您的包含路径中,您应该使用<>.