两个相邻的##运算符
Two adjacent ## operators
有人能解释一下为什么预处理器使用2个串联运算符不会产生任何错误吗?:
#define Z(x) x ## ## 3
Z(3)
结果在:
33
标准说:
替换列表中##预处理令牌的每个实例(不是从参数中(被删除,并且前面的预处理令牌与以下预处理令牌连接
所以我认为前处理程序首先尝试将x
与第二个##
连接起来,这似乎很奇怪。这不会产生任何有效的令牌,所以我希望至少有一个警告。gcc和VC都不会产生任何警告。
我希望能解释一下这是如何工作的以及为什么。Standard提到了placemaker
临时令牌,这可以解释为什么它有效,但在两个"双锐"之间必须有一个这样的令牌。问题是,当参数不包含令牌并且两个concat运算符之间没有参数时,会生成placemaker
令牌。
(C和C++标准分别在§6.10.3.3和§16.3.3中有基本相同的措辞。如果有微小的差异,我引用了C11中的引号。(
##
运算符的处理顺序未明确规定:("##运算符的求值顺序未明确。",第3段最后一句;另见上一节第2段(。所以你不能说"前处理程序首先尝试将x与第二个##连接起来";它可能首先尝试将第一CCD_ 6与CCD_。这也不会产生一个有效的代币,所以这有点狡辩。但重要的是要记住。
问题是,未指定评估顺序的声明是否允许交错评估。换言之,预处理器是否可以通过首先删除第二个##
,然后删除第一个,最后生成单个级联来满足标准?当然,在执行模型中,很明显,未排序的操作被允许交错。(参见§5.1.2.3中的注释13。在C++中,单词是"可以重叠"&门派1.9/13(
这可能有点牵强,但也值得注意的是,在级联之后:
如果结果不是有效的预处理令牌,则行为未定义。
这不是标记为约束,因此不需要错误消息。由于未定义的行为免除了编译器对标准的任何义务,我认为gcc完全有权产生观察到的行为。
简而言之,原始问题中提供的宏替换字符串要么涉及未指定的行为,要么涉及未定义的行为,但不涉及违反约束的行为。因此,编译器没有义务生成诊断。
在这种情况下,不进行诊断可能被视为实施质量问题。另一方面,我不知道有哪个编译器会为##
求值顺序不明确的宏生成警告。除了必须由编译器诊断的宏扩展列表不能以##
标记开始或结束这一约束之外,程序员完全有责任确保级联表达式定义良好。
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 运算符重载 (+),用于添加两个具有 C++ 的数组
- 具有两个间接寻址运算符 (C++) 的函数参数的用途
- 重载运算符 (<<) cout 在 c ++ 中不起作用,当我互相减去两个矩阵时不起作用
- oStream 不打印添加两个 valarray 的结果(使用运算符重载)
- 具有两个或多个模板参数的 C++ assigment 运算符
- 是否允许三元运算符在C++中计算两个操作数?
- 使用运算符重载的两个复数的总和
- 为什么我不能用两个参数重载 C++ 运算符 []?
- 两个相同的重载运算符[]一个返回引用
- 使用运算符+将两个已排序的链表合并到位
- 移位运算符如何在查找两个整数中的不同位数?
- 如何在两个不同的类之间重载"=="运算符?
- 比较两个指针时">="运算符的奇怪行为
- 在不使用 * 和 / 运算符的情况下将两个浮点数相乘和除以
- 如何调用运算符函数添加两个对象?
- 运算符"|"如何计算两个整数?
- 使用转换运算符相互转换两个不同类的对象
- 有两个不同版本的箭头运算符
- 当我有两个对象时<<如何重载运算符?(有关系)