根据this page"替换列表中任意两个连续标识符之间的##运算符在两个标识符"上运行参数替换。也就是说,预处理器操作符##作用于标识符。 Microsoft's page表示",token-string中每次出现的令牌粘贴操作符都被删除,并且它前后的标记被连接在一起#34;。也就是说,预处理器操作符##作用于令牌。
我已经找到了标识符和/或令牌的定义,我找到的最多是this link:"标识符是一个任意长的数字序列,下划线,小写和大写拉丁字母和Unicode字符。有效的标识符必须以非数字字符开头"。
根据该定义,以下宏不适用(在两个帐户上):
#define PROB1(x) x##0000
#define PROB2(x,y) x##y
int PROB1(z) = PROB2( 1, 2 * 3 );
标准是否对##及其作用的对象有一些严格的定义?或者,它主要是尝试看看它是否有效' (a.k.a.实施定义)?
答案 0 :(得分:2)
标准非常精确,包括可以连接的内容以及有效令牌。
en.cppreference.com页面不精确;连接的是预处理标记,而不是标识符。 Microsoft页面更接近标准,但它省略了一些细节,无法区分“预处理令牌”和“令牌”,这些概念略有不同。
标准实际上是什么(§16.3.3/ 3):
对于类似对象和类似函数的宏调用,在重新检查替换列表以替换更多宏名称之前,替换列表中的
##
预处理标记的每个实例(不是来自 删除参数),并将前面的预处理标记与以下预处理标记连接起来。...
作为参考,“预处理令牌”在§2.4中定义为以下之一:
大多数情况下,要组合的标记是标识符(和数字),但很可能通过连接单个字符来生成多字符标记。 (给定可能的预处理器标记列表中的最后一项,任何单个非空白字符都是预处理器标记,即使它不是字母,数字或标准标点符号。)
连接的结果必须是预处理标记:
如果结果不是有效的预处理标记,则行为未定义。生成的令牌可用于进一步的宏替换。
请注意,使用实际参数替换类函数宏的参数名称可能会导致参数名称被0个标记或多个标记替换。如果在连接运算符的任一侧使用该参数:
如果实际参数的标记为零,则不会连接任何内容。 (Microsoft页面暗示连接运算符将连接在它之前和之后的任何令牌。)
在实际参数具有多个标记的情况下,连接的标记是在连接运算符之前或之后的标记。
作为最后一个案例的例子,请记住-42
是两个预处理标记(以及两个标记,在预处理之后): - 和 42 。因此,尽管您可以将pp-number
42E 与pp-number
3 连接起来,从而产生pp-number
(和有效令牌)< kbd> 42E3 ,你不能从 42E 和 -3 创建令牌 42E-3 ,因为只有 - 将被连接,产生两个pp-number
标记: 42E - 3 。 (其中第一个是有效的预处理令牌,但无法将其转换为有效令牌,因此将报告令牌化错误。)
在一系列连接中:
#define concat3(a,b,c) a ## b ## c
未定义连接顺序。因此未指明concat3(42E,-,3)
是否有效;如果前两个标记首先连接在一起,一切都很好,但如果第二个标记首先连接在一起,则结果不是有效的预处理标记。另一方面,concat3(.,.,.)
必须是错误,因为 .. 不是有效令牌,因此{id}}和a##b
都不能被处理。因此,无法使用连接生成令牌b##c
。