连接和标准

时间:2015-05-24 17:45:56

标签: c++ c++11 concatenation c-preprocessor

根据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.实施定义)?

1 个答案:

答案 0 :(得分:2)

标准非常精确,包括可以连接的内容以及有效令牌。

en.cppreference.com页面不精确;连接的是预处理标记,而不是标识符。 Microsoft页面更接近标准,但它省略了一些细节,无法区分“预处理令牌”和“令牌”,这些概念略有不同。

标准实际上是什么(§16.3.3/ 3):

  

对于类似对象和类似函数的宏调用,在重新检查替换列表以替换更多宏名称之前,替换列表中的##预处理标记的每个实例(不是来自   删除参数),并将前面的预处理标记与以下预处理标记连接起来。...

作为参考,“预处理令牌”在§2.4中定义为以下之一:

  • 头名
  • 标识符
  • PP-数
  • 字符字面
  • 用户定义字符的字面
  • 字串文本
  • 用户定义-字串文本
  • 预处理-OP-或-PUNC
  • 每个非白色空格字符,不能是上述
  • 之一

大多数情况下,要组合的标记是标识符(和数字),但很可能通过连接单个字符来生成多字符标记。 (给定可能的预处理器标记列表中的最后一项,任何单个非空白字符都是预处理器标记,即使它不是字母,数字或标准标点符号。)

连接的结果必须是预处理标记:

  

如果结果不是有效的预处理标记,则行为未定义。生成的令牌可用于进一步的宏替换。

请注意,使用实际参数替换类函数宏的参数名称可能会导致参数名称被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