正如您所注意到的,# 仅在类似函数的宏中具有定义的效果。 § 6.10.3.2/1(所有对标准的引用均来自 C11 草案 (N1570))。要了解在类对象宏中发生了什么,我们必须看看别处。
表单的预处理指令
# define identifier replacement-list new-line
定义一个
类对象宏
这会导致宏名称的每个后续实例
由构成
指令的其余部分。 [...]
§ 6.10.3/9
因此,唯一的问题是# 是否允许在replacement-list 中。如果是,则照常参与替换。
我们在 § 6.10/1 中找到语法:
replacement-list:
pp-tokens (opt.)
pp-tokens:
preprocessing-token
pp-tokens preprocessing-token
现在,# 是有效的preprocessing-token 吗? § 6.4/1 说:
preprocessing-token:
header-name
identifier
pp-number
character-constant
string-literal
punctuator
each non-white-space character that cannot be one of the above
这当然不是header-name(§ 6.4.7/1),identifier 令牌中不允许使用它(§ 6.4.2.1/1),也不是pp-number(基本上是允许格式的任何数字,§ 6.4.8/1),也不是character-constant(例如u'c',§ 6.4 .4.4/1)或string-literal(正是您所期望的,例如L"String",§ 6.4.5/1)。
但是,它在 § 6.4.6/1 中被列为 punctuator。因此,它在类对象宏的replacement-list 中是允许的,并且将被逐字复制。它现在需要重新扫描,如 § 6.10.3.4 中所述。让我们看看你的例子:
C(A) 将替换为 C(X#Y)。 #这里没有特殊作用,因为它不在C的replacement-list中,而是在它的参数中。 C(X#Y) 显然变成了B(X#Y)。然后B 的参数通过B 的replacement-list 中的# 运算符转换为字符串文字,产生"X#Y"
因此,您没有未定义的行为。