17.6.2.2 标头 [using.headers]
3)翻译单元应仅在任何外部声明或定义之外包含标头,并且应在该翻译单元中对在该标头中声明的任何实体的第一次引用之前在词法上包含标头。
如果我的解释是正确的:
extern "C"
{
#include "smth.h"
}
是非法的(那里有很多)。
我误解了吗?“外部声明”和“外部定义”是否意味着其他东西(在这种情况下)?
3)翻译单元应仅在任何外部声明或定义之外包含标头,并且应在该翻译单元中对在该标头中声明的任何实体的第一次引用之前在词法上包含标头。
如果我的解释是正确的:
extern "C"
{
#include "smth.h"
}
是非法的(那里有很多)。
我误解了吗?“外部声明”和“外部定义”是否意味着其他东西(在这种情况下)?
标头在标准中定义为声明或定义 C++ 标准库的元素的位置。这不包括用户的.h
文件。见§17.6.1.2:
C++ 标准库的每个元素都在标头中声明或定义(视情况而定)。
C++ 标准库提供了 52 个C++ 库头文件,如表 14 所示。
C 标准库的功能在 26 个附加头文件中提供,如表 15 所示。
所以你给出的例子很好(只要.h
文件本身不包含一些标准库头文件)。但是,这不会是:
extern "C"
{
#include <string>
}
为了进一步支持头文件和源文件(包括.h
文件)之间的分离,#include
预处理指令被定义为在语法中包含“头文件”并在<h-char-sequence>
语法中包含“源文件” "q-char-sequence"
(第 16.2 节)。所以你#include "smth.h"
包括一个源文件,而不是一个标题。源文件被定义为程序的文本,它与包含的标题一起构成一个翻译单元:
程序的文本保存在本国际标准中称为源文件的单元中。源文件连同通过预处理指令包含的所有头文件 (17.6.1.2) 和源文件 (16.2)
#include
,减去由任何条件包含 (16.1) 预处理指令跳过的任何源代码行,称为翻译单元。
我认为这种措辞的目的是将标准库头文件与其表示分离。没有理由将它们存储为 C++ 源文件,即使它们通常是。包含一个特定的头文件必须具有使程序可以使用适当的声明的效果。
我认为Joseph Mansfield 的回答大部分正确地解释了“标头”一词(它仅指第 17.6.1.2 节 [标头] 中定义的标准库标头),除了我认为它还包括附录 D 中描述的 C 标头,作为脚注176(在 N3936 中)将<iso646.h>
和都<ciso646>
称为“标准头”。此答案侧重于短语external declaration 或 definition的含义。
看来 C++ 标准中的这个特定句子是从 C 标准借来的。至少根据这个非官方版本, C89 标准中似乎存在相同的措辞,并且在 C99 和 C11 中都存在。
虽然 C++ 标准没有定义“外部声明或定义”,但这些术语在 C 标准中定义(引用 N1256 §6.9):
预处理后的程序文本单元是一个翻译单元,它由一系列外部声明组成。这些被描述为“外部”,因为它们出现在任何函数之外(因此具有文件范围)。[...]
外部定义是一个外部声明,也是函数或对象的定义。
翻译单元的 C 语法是:
translation-unit:
external-declaration
translation-unit external-declaration
external-declaration:
function-definition
declaration
现在,C++ 标准中翻译单元的相应描述是(§3.5 [basic.link]):
一个翻译单元由一系列声明组成。
语法很简单
translation-unit:
declaration-seq_opt
简而言之,在 C 中,翻译单元是一个外部声明序列,而在 C++ 中,它是一个声明序列。因此,C++ 标准中的“外部”一词似乎是借用造成的历史产物,引用的句子旨在禁止在任何声明或任何类型的定义中包含标准库头文件。