16

它在 C++ std 16.3.4 中说:

重新扫描生成的预处理标记序列 [来自宏调用替换],以及源文件的所有后续预处理标记,以替换更多宏名称。

如果在替换列表的扫描过程中找到被替换的宏的名称(不包括源文件的其余预处理标记),则不会替换它。

此外,如果任何嵌套替换遇到被替换的宏的名称,它不会被替换。

这些未替换的宏名称预处理标记不再可用于进一步替换,即使它们稍后在该宏名称预处理标记将被替换的上下文中进行(重新)检查。

嵌套宏替换到底是什么?

具体考虑:

#define f(x) 1 x
#define g(x) 2 x

g(f)(g)(3)

我本来期望以下内容:

g(f)(g)(3)    <-- first replacement of g, ok
2 f(g)(3)     <-- nested replacement of f, ok
2 1 g(3)      <-- second nested replacement of g, don't replace, stop

然而 gcc 出人意料地进行了 g 的第二次替换,产生:

2 1 2 3

有任何想法吗?

更新:

经过大量研究,让我用一个更简单的例子来澄清这个问题:

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

A(i)(j)

这扩展如下:

A(i)(j)
B(j)
A(j)

该标准没有规定是否A(j)应扩展为B。委员会决定以这种方式保留它,因为不期望现实世界的程序依赖于这种行为,因此A(j)未扩展和扩展A(j)B被认为是一致的。

4

3 回答 3

6

这解释了最初的意图,以及为什么没有在标准中添加关于此主题的说明:

http://open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#268


268. 重新扫描替换文本中的宏名称抑制

部分:16.3.4 [cpp.rescan]     状态:打开     提交者:Bjarne Stroustrup     日期:2001 年 1 月 18 日

从标准中不清楚以下示例的结果应该是什么:

#define NIL(xxx) xxx
#define G_0(arg) NIL(G_1)(arg)
#define G_1(arg) NIL(arg)
G_0(42)

标准中的相关文本可在 16.3.4 [cpp.rescan] 第 2 段中找到:

[剪断]

的展开顺序G0(42)如下:

G0(42)
NIL(G_1)(42)
G_1(42)
NIL(42)

问题是NIL在该序列的最后一行中的使用是否符合引用文本下的非替换条件。如果是这样,结果将是NIL(42)。如果没有,结果将是简单的42

本文中 J11 委员会的初衷是结果应该是42,正如其作者 Dave Prosser 提供的替换算法的原始伪代码描述所证明的那样。然而,英文描述省略了伪代码的一些微妙之处,因此可以说对这种情况给出了错误的答案。

建议的解决方案(迈克米勒):[剪断]

2004 年 4 月 WG14 会议的笔记(来自 Tom Plum):

早在 1980 年代,几位 WG14 人员就明白,“不可替代”的措辞与产生伪代码的尝试之间存在微小差异。委员会的决定是,没有任何现实的程序“在野外”会冒险进入这个领域,并且试图减少不确定性是不值得冒改变实现或程序的一致性状态的风险。

于 2013-03-28T00:45:11.890 回答
0

g(x) is always replaced by 2 x. In the second nested replacement of g, you call g(x) with x=3, so this produces the result 2 3. My understanding is that the compiler doesn't replace the macro with it's value, in case this produces an infinite loop:

#define g( x ) f( x )
#define f( x ) g( x )

g( 1 ) -- > f( 1 ) --> stop 
于 2013-03-23T07:43:32.640 回答
0

因为它提到“(不包括源文件的其余预处理标记)。” gonly sees的第一个替换f。现在f有资格用取自文件其余部分的预处理标记替换,但fg. 替换f(g)产生 ag同样不受递归污染。

嵌套替换是由另一个替换包围的替换,而不是其令牌来自另一个替换的替换。根据后一种定义,一个替换可以有几个互斥的嵌套父母。

于 2013-05-22T08:26:26.027 回答