考虑以下宏定义和调用:
#define x x[0]
#define y(arg) arg
y(x)
此调用扩展为x[0]
(在Visual C ++ 2010,g ++ 4.1,mcpp 2.7.2和Wave上测试)。
为什么?具体来说,为什么它不会扩展到x[0][0]
?
在宏替换期间,
替换列表中的参数...在扩展了其中包含的所有宏之后被相应的参数替换。在被替换之前,每个参数的预处理标记都被完全宏替换(C ++03§16.3.1/ 1)。
评估宏调用,我们采取以下步骤:
y
,x
作为其arg
参数的参数x
已被宏替换为x[0]
arg
将替换为参数的宏替换值x[0]
替换所有参数后的替换列表为x[0]
。
替换列表中的所有参数都被替换后,重新扫描生成的预处理标记序列...以替换更多的宏名称(C ++03§16.3.4/ 1)。
如果在替换列表的扫描过程中找到要替换的宏的名称,则不会替换它。此外,如果任何嵌套替换遇到要替换的宏的名称,则不会替换它(C ++03§16.3.4/ 2)。
重新扫描替换列表x[0]
(请注意要替换的宏的名称是y
):
x
被标识为类似对象的宏调用x
已被x[0]
此时替换停止,因为§16.3.4/ 2中的规则阻止了递归。重新扫描后的替换列表为x[0][0]
。
我清楚地误解了一些东西,因为我测试的所有预处理器都说我错了。此外,这个例子是C ++ 0x FCD中的一个更大的例子(§16.3.5/ 5),它也说预期的替换是x[0]
。
为什么在重新扫描期间x
没有被替换?
C99和C ++ 0x实际上与引用部分中的C ++ 03具有相同的措辞。
答案 0 :(得分:17)
我相信你引用了关键段落,你刚刚停止了。 16.3.4 / 2(强调我的):
如果在此扫描期间找到要替换的宏的名称 替换清单(不包括 源文件的其余部分 预处理令牌),它不是 更换。此外,如果有任何嵌套 替换遭遇 要替换的宏的名称, 它没有被替换。 这些无法替代 宏名称预处理令牌是否定的 更长时间可用 更换即使他们以后 (重新)在其中检查 宏名称预处理令牌会 否则已被替换。
因此,在x
的参数替换期间x[0]
替换为y
时,它会被完全宏替换,这意味着它会在那时重新扫描,{{1} }被递归规则捕获。这意味着x
中的x
不再有资格进行进一步替换,包括在重新扫描x[0]
的部分扩展结果期间。