串联和标准

Concatenation and the standard

根据 this page "A ## operator between any two successive identifiers in the replacement-list runs parameter replacement on the two identifiers". That is, the preprocessor operator ## acts on identifiers. Microsoft's page 所说,“删除标记字符串中每次出现的标记粘贴运算符,并连接其前后的标记”。也就是说,预处理器运算符##作用于标记。

我一直在寻找标识符 and/or 标记的定义,我找到的最多的是 this link:"An identifier is an arbitrary long sequence of digits, underscores, lowercase and uppercase Latin letters, and Unicode characters. A valid identifier must begin with a non-digit character".

根据该定义,以下宏应该不起作用(在两个帐户上):

#define PROB1(x) x##0000
#define PROB2(x,y) x##y
int PROB1(z) = PROB2( 1, 2 * 3 );

该标准是否对## 及其作用的对象有一些严格的定义?或者,它主要是 'try and see if it works'(a.k.a。实现已定义)?

该标准非常精确,无论是关于什么可以连接,还是关于什么是有效令牌。

en.cppreference.com页面不准确;连接起来的是预处理标记,而不是标识符。 Microsoft 页面更接近标准,尽管它省略了一些细节并且未能区分 "preprocessing token" 和 "token",这两个概念略有不同。

标准的实际内容 (§16.3.3/3):

For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token.…

供参考,"preprocessing token" 在 §2.4 中定义为以下之一:

  • header-name
  • 标识符
  • pp-number
  • character-literal
  • 用户自定义-character-literal
  • string-literal
  • 用户自定义-string-literal
  • preprocessing-op-or-punc
  • 每个 non-white-space 个不能是上述之一的字符

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

串联的结果必须是预处理标记:

If the result is not a valid preprocessing token, the behavior is undefined. The resulting token is available for further macro replacement.

请注意,用实际参数替换 function-like 宏的参数名称可能会导致参数名称被 0 个标记或多个标记替换。如果在连接运算符的任一侧使用该参数:

  • 如果实际参数有零个标记,则不会连接任何内容。 (Microsoft 页面暗示连接运算符将连接它之前和之后的任何标记。)

  • 如果实际参数有多个标记,则连接的是连接运算符之前或之后的那个。

作为最后一种情况的示例,请记住 -42 是两个预处理标记(以及预处理后的两个标记):- 42。因此,虽然您可以将 pp-number 42Epp-number 3 连接起来,但结果是 pp-number (和有效令牌)42E3,您不能从 42E 和 [= 创建令牌 42E-3 61=]-3,因为只有 - 会被连接起来,导致两个 pp-number 标记:42E-3。 (其中第一个是有效的预处理令牌,但无法转换为有效令牌,因此会报告令牌化错误。)

在串联序列中:

#define concat3(a,b,c) a ## b ## c

未定义连接顺序。所以未指定 concat3(42E,-,3) 是否有效;如果先连接前两个标记,则一切正常,但如果先连接后两个标记,则结果不是有效的预处理标记。另一方面,concat3(.,.,.) 一定是一个错误,因为 .. 不是一个有效的标记,所以 a##bb##c 都不能是处理。所以不可能通过连接产生令牌...