【问题标题】:Two adjacent ## operators两个相邻的 ## 运算符
【发布时间】:2015-07-15 14:22:33
【问题描述】:

谁能解释一下为什么有 2 个连接运算符不会导致预处理器产生任何错误?:

#define Z(x) x ## ## 3
Z(3)

结果:

33

标准是这样说的:

...替换列表中 ## 预处理标记的每个实例 (不是来自参数)被删除并且前面的预处理 令牌与以下预处理令牌连接

所以我希望预处理器首先尝试将x 与第二个## 连接起来,这看起来很奇怪。这不会产生任何有效的令牌,所以我希望至少有一个警告。 gcc 和 VC 都不会产生任何警告。

我希望能对它的工作原理和原因进行一些解释。 标准提到了placemaker 临时标记,这可以解释为什么它有效,但是在两个“双锐”之间必须有一个这样的标记。问题是placemaker在参数不包含token且两个concat操作符之间没有参数时会生成token。

【问题讨论】:

标签: c++ c c-preprocessor


【解决方案1】:

(C 和 C++ 标准分别在 §6.10.3.3 和 §16.3.3 中具有基本相同的措辞。如果有细微差别,我引用了 C11 中的引号。)

## 运算符的处理顺序未明确指定:(“## 运算符的求值顺序未指定。”,第 3 段的最后一句;另请参见上一节的第 2 段)。所以你不能说“预处理器首先尝试将x与第二个##连接起来”;它可能首先尝试将第一个##3 连接起来。这也不会产生有效的令牌,所以它有点狡辩。但记住这一点很重要。

问题是未指定评估顺序的声明是否允许交错进行评估。换句话说,预处理器是否可以通过首先删除第二个##,然后删除第一个,最后产生一个连接来满足标准?当然,在执行模型中,很明显 unsequenced 操作是允许交错的。 (参见第 5.1.2.3 节中的注释 13。在 C++ 中,这些词是“可以重叠”;第 1.9/13 节)

这可能有点牵强,但也值得注意的是,在串联之后:

如果结果不是有效的预处理标记,则行为未定义。

没有标记为约束,因此不需要错误消息。而且由于未定义的行为使编译器免除了对标准的任何义务,我认为 gcc 完全有权产生观察到的行为。

简而言之,原始问题中提供的宏替换字符串涉及未指定或未定义的行为,但不违反约束。因此,编译器没有义务生成诊断。

在这种情况下未生成诊断可能被视为实施质量问题。另一方面,我不知道有任何编译器会对具有不明确的## 评估顺序的宏产生警告。除了宏扩展列表不能以 ## 标记开始或结束(必须由编译器诊断)这一约束之外,确保连接表达式的定义明确是程序员的全部责任。

【讨论】:

  • “我不知道有任何编译器会对 ## 评估顺序不明确的宏产生警告。”既然 PP 连接是关联的(假设中间标记是有效的)并且没有可观察到的副作用,那么这有什么意义呢?
  • @arnevogel:中间令牌可能对某些订单无效。 2e ## + ## 7
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-30
相关资源
最近更新 更多