为什么在重新扫描期间不会替换参数替换?

Jam*_*lis 12 c++ c-preprocessor

请考虑以下宏定义和调用:

#define x x[0]
#define y(arg) arg

y(x)
Run Code Online (Sandbox Code Playgroud)

此调用扩展为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具有相同的措辞.

Ste*_*sop 17

我相信你已经引用了关键段落,你刚刚停止了.16.3.4/2(强调我的):

如果在替换列表的扫描期间找到要替换的宏的名称(不包括源文件的其余预处理标记),则不会替换它.此外,如果任何嵌套替换遇到要替换的宏的名称,则不会替换它.这些未替换的宏名称预处理令牌不再可用于进一步替换,即使它们稍后(重新)检查在其中否则将替换该宏名称预处理令牌的上下文中.

所以,当x被替换x[0]的参数替换时y,它完全取代宏,这意味着它是在这一点上重新扫描,并x通过递归规则抓获.这意味着xin x[0]不再有资格进行进一步替换,包括在重新扫描部分扩展结果期间y(x).