两个相邻的##运算符

Two adjacent ## operators

本文关键字:运算符 两个      更新时间:2023-10-16

有人能解释一下为什么预处理器使用2个串联运算符不会产生任何错误吗?:

#define Z(x) x ## ## 3
Z(3)

结果在:

33

标准说:

替换列表中##预处理令牌的每个实例(不是从参数中(被删除,并且前面的预处理令牌与以下预处理令牌连接

所以我认为前处理程序首先尝试将x与第二个##连接起来,这似乎很奇怪。这不会产生任何有效的令牌,所以我希望至少有一个警告。gcc和VC都不会产生任何警告。

我希望能解释一下这是如何工作的以及为什么。Standard提到了placemaker临时令牌,这可以解释为什么它有效,但在两个"双锐"之间必须有一个这样的令牌。问题是,当参数不包含令牌并且两个concat运算符之间没有参数时,会生成placemaker令牌。

(C和C++标准分别在§6.10.3.3和§16.3.3中有基本相同的措辞。如果有微小的差异,我引用了C11中的引号。(

##运算符的处理顺序未明确规定:("##运算符的求值顺序未明确。",第3段最后一句;另见上一节第2段(。所以你不能说"前处理程序首先尝试将x与第二个##连接起来";它可能首先尝试将第一CCD_ 6与CCD_。这也不会产生一个有效的代币,所以这有点狡辩。但重要的是要记住。

问题是,未指定评估顺序的声明是否允许交错评估。换言之,预处理器是否可以通过首先删除第二个##,然后删除第一个,最后生成单个级联来满足标准?当然,在执行模型中,很明显,未排序的操作被允许交错。(参见§5.1.2.3中的注释13。在C++中,单词是"可以重叠"&门派1.9/13(

这可能有点牵强,但也值得注意的是,在级联之后:

如果结果不是有效的预处理令牌,则行为未定义。

这不是标记为约束,因此不需要错误消息。由于未定义的行为免除了编译器对标准的任何义务,我认为gcc完全有权产生观察到的行为。

简而言之,原始问题中提供的宏替换字符串要么涉及未指定的行为,要么涉及未定义的行为,但不涉及违反约束的行为。因此,编译器没有义务生成诊断。

在这种情况下,不进行诊断可能被视为实施质量问题。另一方面,我不知道有哪个编译器会为##求值顺序不明确的宏生成警告。除了必须由编译器诊断的宏扩展列表不能以##标记开始或结束这一约束之外,程序员完全有责任确保级联表达式定义良好。