这个 "not a header-name" 是符合C++语法的吗?

5
考虑以下代码片段。
#define X iostream>
#include <X

#include之后,最长的字符序列,可以构成预处理标记<,并且后面跟着一个标识符。在预处理期间,该标识符将被替换为iostream>,并且在所有替换完成后,该指令与形式< h-char-sequence >匹配。因此,程序应该是良好形式的。
另一方面,lex.phases指出:

源文件不得以部分预处理标记或部分注释结尾。

据我所知,上述程序并不以部分预处理令牌结束。然而,脚注中提到:

部分预处理令牌可能来自于以需要终止字符序列的多字符令牌的第一部分结束的源文件,比如缺少闭合的">头文件名

这让我感到困惑。源文件可以以缺少闭合的>头文件名结束吗?
看起来 msvc 和 gcc 对这个代码片段 differently 的处理方式不同。

所以缺少闭合的>,所以根据你自己的引用,它是无效的。为什么会有人犯下这样的罪行,这是另一个谜。 - undefined
1
我认为这是标准中的一个错误。它明确允许在#include指令中使用宏(包括像这样的宏),但是你引用的部分却说整个文件都是无效的(甚至在宏展开之前)。因此,如果在该包含之后有任何内容(比如空注释),那么它将是有效的,但现在的情况是无效的。 - undefined
@Yksisarvinen 我认为你上面的评论以及现在已删除的回答解释为什么如果文件没有以那种方式结束也是可以的,这将是一个很好的答案。 - undefined
1
@TedLyngmo 我已经改变了四次主意,但我仍然不确定解释是否百分之百正确。我重新写了我的答案并取消了删除,但我仍然觉得我上面的评论也可能是一个合理的解释。 - undefined
1个回答

4

我相信这段代码是有效的。[lex.phases]#1.10(对于[lex.phases]#1.3的脚注)在这里不适用。我们这里没有header-name,我们有序列#include pp-tokens new-line,根据cpp.include#4,它是有效且完整的标记1(只要有换行符)。

老实说,我不确定在#include指令的情况下部分预处理标记会是什么样子 - 唯一可能缺失的是换行符,因为pp-tokens几乎可以匹配任何内容。这似乎是意外的交互或某个时候的变化结果。


注意:如果在宏展开后,这将不是一个有效的#include指令,行为是未定义的。

1
在[lex.phases/2]中修正了缺失的换行符。考虑使用#include <"。由于双引号本身不是有效的预处理记号,这只会匹配头文件名的语法,但是它是不完整的。这是我能想到的最好的解决办法。 - undefined
h-char是除了换行符和>之外的任何源字符,大多数这些字符的序列都可以形成有效的pp-token序列,除了一些复合情况,比如字符串和字符...所以例如#include<"\"是另一个例子,因为"\"无法匹配字符串文字的语法。 - undefined
@JeffGarrett 所有这些仍然可以形成一个完整的预处理标记序列,其中"匹配一个单字符非空白预处理标记。然而,如果任何"'被词法分析为该类别,程序已经具有未定义的行为,参考https://eel.is/c++draft/lex.pptoken#2.sentence-4。 - undefined
我承认你是对的!我觉得允许解析成pp-tokens然后立即导致UB,与无法匹配这种可能性相比有点不直观...但措辞更合适。在这种情况下,我想不出任何例子。 - undefined
1
@JeffGarrett 小修正:根据我发布的链接中的文本,它现在是格式不正确的。直到最近它还是(奇怪地)UB。然而,非空白单字符标记自C++98以来就存在,部分预处理标记的规则也是如此。我也不知道它应该涵盖什么情况。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接