头文件名(如 bits/vector.tcc 标准)是否兼容

Are header file names such as bits/vector.tcc standard compliant?

本文关键字:标准 是否 tcc vector 文件名 bits      更新时间:2023-10-16

在我的代码库中,我将高度模板化代码的实现细节'隐藏'在bits子目录中的.tcc文件中,即

// file inc/foo.h:
#ifndef my_foo_h          // include guard
#define my_foo_h
namespace my {
  /* ... */               // templated code available for user
}
#include "bits/foo.tcc"   // includes implementation details
namespace my {
  /* ... */               // more templated code using details from foo.tcc
}
#endif
// file inc/bits/foo.tcc:
#ifndef my_foo_tcc        // include guard
#define my_foo_tcc
#ifndef my_foo_h
#  error foo.tcc must be #included from foo.h
#endif
namespace my { namespace details {
  /* ... */               // defails needed in foo.h
} }
#endif

当然,包含路径中只能有一个文件bits/foo.tcc。否则,将发生冲突和(希望(编译错误。这刚刚发生在我身上的 bits/vector.tcc ,它包含在 gcc 的 (4.8( vector 中,但也包含在我自己的标头中(使用 #include "bits/vector.tcc" 而不是 #include <bits/vector.h> (。

我的问题:这在形式上是 gcc 的错误(因为它使用的名称bits/vector.tcc不受标准保护(还是正确的,即甚至是正式的我的错?如果是后者,保证可以使用头文件的哪些名称?

(注意,我不想听到关于如何避免这种情况的明显建议(。


编辑 问题是标准库(由编译器提供(提供的头文件vector有一个预处理器指令#include <bits/vector.tcc>,这会导致预处理器加载我的文件,而不是随标准库提供的文件。

以下是

C++11标准[cpp.include]对此的看法:

1 #include指令应标识可由实现处理的头文件或源文件。

2 表单的预处理指令

# include < h-char-sequence> new-line

在实现定义的位置序列中搜索由指定序列唯一标识的标头 在<>分隔符之间,并导致该指令被整个内容替换 的标头。如何指定位置或标识标头由实现定义。

3 表单的预处理指令

# include " q-char-sequence" new-line

导致该指令被指定标识的源文件的全部内容替换 "分隔符之间的序列。在实现定义的文件中搜索命名源文件 方式。如果不支持此搜索,或者搜索失败,则重新处理指令,就像读取

# include < h-char-sequence> new-line

具有与原始指令相同的包含序列(包括>字符,如果有(。

换句话说,#include < >仅用于搜索标头标头是标准库提供的功能之一。我说"东西"是因为标准没有指定它是什么 - 它根本不需要文件(尽管我知道的所有编译器都将标头实现为文件(。

#include " "用于"其他一切" - 就标准而言,它们都是"源文件",尽管在一般情况下,我们通常将旨在#include d的文件称为"头文件"。另请注意,如果未找到此类源文件,则将搜索(标准库(标头。

因此,在您的情况下:

  • 该标准没有说任何像bits/vector.tcc这样的文件;事实上,它没有说任何文件。所有这些都属于"实现定义"标题,因此取决于您的编译器及其文档。

    同时(感谢@JamesKanze在评论中指出这一点(,该标准明确规定了#include <vector>应该做什么,并且从未提及它可能取决于文件的存在或不存在。所以在这方面,gcc加载你的bits/vector.tcc而不是它自己的是一个gcc错误。如果 gcc 加载自己的bits/vector.tcc而不是你的,它将在其"实现定义"范围内。

  • #include "vector"主要用于包含名为 vector 的源文件。但是,如果未找到此类文件,则效果与包含标准标头<vector>相同(这会导致类模板std::vector被视为已定义(。

标准非常开放,但是...包括<vector>应该工作;我没有看到任何授权它不(提供你已经做了#include <vector>,而不是#include "vector"(,无论您的个人姓名如何。

更一般地说,或多或少通用的算法搜索标头是首先在目录中搜索包含执行包含的文件。 这是完成的正是为了避免您遇到的问题类型。不这样做(或不使用其他机制来确保包含 从标准标头 找到他们应该的文件to( 是编译器中的错误。 一个严重的,恕我直言。 (的当然,编译器可能会记录某些选项引入的内容某些限制,或者您需要使用某些选项使其以标准方式运行。 我不认为 g++文档-I与标准标题不兼容,但它确实说如果你使用-iquote,它不应该影响任何包括使用 <...> .(

编辑:

上面的第二段实际上只适用于"..."包含的形式。 #include <vector>应该找到标准标头,即使您在同一文件中vector文件目录作为您正在编译的文件。

在缺乏-I选项的情况下,这是有效的。 普遍但是,"-I"选项会将目录添加到搜索列表中对于这两种类型的包含。 原因是你,作为开发人员,可能会想要对待各种第三方库(例如X-Windows(,就好像它们是系统的一部分一样井。 (我认为Linux确实将X-Windows作为系统的一部分,将其标题放在/usr/include中,但这不是通常的过去其他尤尼斯的情况。 所以你用-I来指定他们,以及你的其他包含目录。 如果你在您的其他目录中vector一个文件,它将"覆盖"系统一。

这显然是一个缺陷:如果我没记错的话(但它已经一段时间(,G++ 曾经确实有额外的选项可以放列表中仅包含一种类型的目录。 而在现代 gcc/g++,有 -iquote(和 -I- ,它指定所有较早的-I选项都适用于"..."仅包括(。 然而,这些功能很少被使用,因为 GCC/G++ 是唯一支持它们的编译器。

考虑到所有这些,gcc/g ++处理可能是最好的可以希望。 错误不在于编译器本身,而在于库标头,当它使用 <bits/vector.tcc>绝对希望包含文件与文件执行包含操作。 (另一种说法是 bits/vector.tcc 不是系统标头,在任何意义上word,但系统库的实现标头。

这些都对原始海报没有多大帮助,除非他觉得就像修改 G++ 的库头一样。 (如果可移植性没有任何问题,他不考虑将他的头球作为系统,他可以将-I更改为-iquote