宏参数的扩展如何在 C++ 中工作

How does expansion of the macro parameters work in c++

我刚刚注意到一个有趣的事情,关于 C++ 中宏参数的扩展。

我定义了4个宏;其中 2 个将给定参数转换为字符串,另外 2 个尝试分隔 2 个参数。我用扩展为 , 的宏向他们传递参数并得到以下结果:

#define Quote(x) #x
#define String(x) Quote(x)
#define SeparateImpl(first, second) first + second
#define Separate(pair) SeparateImpl(pair)
#define comma ,

int main(){
Quote(1 comma 2);  // -> "1 comma 2"
String(1 comma 2); // -> "1 , 2"
SeparateImpl(1 comma 2); // -> 1 , 2 + *empty arg*
Separate(1 comma 2);     // -> 1 , 2 + *empty arg*
return 0;
}

所以,当我们看到宏 String 变成了 "1 , 2",这意味着宏逗号首先被解包了。但是,宏Separate变成了1 , 2 + **empty arg**,也就是说宏comma没有先解包,请问这是为什么?我在 VS2019 中尝试过这个。

#define Quote(x) #x
#define String(x) Quote(x)
#define SeparateImpl(first, second) first + second
#define Separate(pair) SeparateImpl(pair)
#define comma ,

宏调用过程如下:

  • 参数替换 (a.s),如果在替换列表中提到了一个参数并且该参数不参与粘贴或字符串化,则它被完全展开并在替换中提到该参数列表被结果替换。
  • 字符串化
  • 粘贴
  • 重新扫描并进一步替换(r.a.f.r),重新扫描生成的替换列表,在此期间宏的名称被标记为扩展无效("painted blue")。

每个案例的展开方式如下:

Quote(1 comma 2)

a.s。没有动作(只提到参数是字符串化)。字符串化适用。结果:"1 comma 2".

String(1 comma 2)

a.s。适用;屈服 Quote(1 , 2)。 r.a.f.r期间,Quote被识别为宏,但参数计数不匹配。这是无效的。但见下文。

SeparateImpl(1 comma 2)

宏调用无效。宏是用一个参数调用的,但它应该有 2 个参数。请注意,comma 被定义为宏是无关紧要的;在宏调用级别,您只是在查看标记。

Separate(1 comma 2)

a.s。适用;屈服 SeparateImpl(1 , 2)。在 r.a.f.r. 期间,SeparateImpl 被调用...该调用的 a.s。适用,产生 1 + 2.

I tried this in VS2019.

我一眼就能看出它是 VS 2020 年之前的东西,墙壁告诉我他们最终将致力于预处理器合规性。特别是 VS 似乎有这种奇怪的状态,其中带有逗号的标记仍然被视为单个参数(就好像参数识别发生在扩展之前但继续应用或其他东西);所以在这种情况下,1 , 2 将是您 String(1 comma 2) 调用中的那个奇怪的东西;即,Quote 是用 1 , 2 调用的,但在那种情况下,它实际上是一个参数。