串联和标准
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
42E 与 pp-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##b
和 b##c
都不能是处理。所以不可能通过连接产生令牌...
。
根据 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
42E 与 pp-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##b
和 b##c
都不能是处理。所以不可能通过连接产生令牌...
。