C预处理程序中的递归是否滥用标准中的不一致之处?

问题描述

考虑以下代码

#define MAP_OUT

#define A(x) B MAP_OUT (x)
#define B(x) A MAP_OUT (x)

A(x)

然后A(x)扩展到B MAP_OUT (x),然后扩展到B (x)。现在来看一下标准:

替换列表中的所有参数并进行#和##处理后,将删除所有地标预处理标记。然后,对生成的预处理令牌序列以及源文件的所有后续预处理令牌进行重新扫描,以替换更多的宏名称

B (x)是否属于“ “导致更多的宏名称替换的预处理令牌序列” ?我尝试过的所有编译器都不会在一次扫描中扩展B (x),但是标准本身呢?

解决方法

您在摘樱桃。该标准要求停止重新扫描和更换。

还有其他段落,自C ++ 98以来,每个C ++标准中的措词都相同(不仅仅是您引用的内容),实际上控制着您观察到的行为。

替换了替换列表中的所有参数之后,将使用源文件的所有后续预处理令牌重新扫描所得的预处理令牌序列,以替换更多的宏名称。

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

正如我所说,措辞在每个C ++标准中都是相同的。只有部分和段落编号会更改。

  • 在C ++ 98中,以上引用为第16.3.4节“重新扫描和进一步替换”,第1和2段;
  • 在C ++ 17中,以上引用为第19.3.4节“重新扫描和进一步替换”,第1和第2段;
  • 在最新的C ++ 20草案(至少是我访问的最新版本)中,以上引用是第15.6.4节“重新扫描和进一步替换”,第1和第3段(在第2段中添加了一个说明性示例,而不是规范性文本)。
,

B(x)是否属于“导致预处理令牌序列以替换更多的宏名称”?

不,绝对不是。重读:

替换列表中的所有参数并进行#和##处理后,将删除所有地标预处理标记。然后重新扫描生成的预处理令牌序列。

由参数替换in A(x)产生的预处理令牌序列恰好是B MAP_OUT (x),仅此而已。然后扫描此序列一次,以查找更多要替换的宏。只能替换一个合格的宏MAP_OUT。然后扫描MAP_OUT的替换项,未发现任何内容,然后恢复处理。

没有任何迹象表明B中的B MAP_OUT (x)应该被扫描两次。