C++17 赋值排序:GCC 中仍未实现?

2024-02-15

我尝试了以下代码作为实现交换的天真的尝试R and B中的字节数ABGR word

#include <stdio.h>
#include <stdint.h>

uint32_t ABGR_to_ARGB(uint32_t abgr)
{
  return ((abgr ^= (abgr >> 16) & 0xFF) ^= (abgr & 0xFF) << 16) ^= (abgr >> 16) & 0xFF;
}

int main() 
{
    uint32_t tmp = 0x11223344;
    printf("%x %x\n", tmp, ABGR_to_ARGB(tmp));
}

令我惊讶的是,这段代码在 C++17 模式下的 GCC 中“有效”——字节被交换

http://coliru.stacked-crooked.com/a/43d0fc47f5539746 http://coliru.stacked-crooked.com/a/43d0fc47f5539746

但它不应该交换字节! C++17 明确指出,赋值的 RHS 应该在 LHS 之前[完全]排序,这也适用于复合赋值。这意味着在上面的表达式中,每个 RHS^=应该使用原始值abgr。因此最终的结果是abgr应该简单地有B字节异或R字节。这就是 Clang 产生的结果(有趣的是,带有排序警告)

http://coliru.stacked-crooked.com/a/eb9bdc8ced1b5f13 http://coliru.stacked-crooked.com/a/eb9bdc8ced1b5f13

快速浏览 GCC 汇编

https://godbolt.org/g/1hsW5a https://godbolt.org/g/1hsW5a

揭示它似乎按倒序排列:LHS 在 RHS 之前。这是一个错误吗?或者这是海湾合作委员会有意识的决定?或者我误解了什么?


表现出完全相同的行为int a = 1; (a += a) += a;,GCC 计算出a == 4然后叮叮当当a == 3.

潜在的歧义源于标准的这一部分(来自工作草案 N4762):

[expr.ass]:7.6.18 赋值和复合赋值运算符

第 1 段:赋值运算符 (=) 和复合赋值运算符都是从右到左分组的。都需要一个 可修改的左值作为其左操作数;它们的结果是引用左操作数的左值。结果全部 如果左操作数是位域,则 case 是位域。在所有情况下,赋值都在值之后排序 右操作数和左操作数的计算,以及赋值表达式的值计算之前。 右操作数在左操作数之前排序。对于不确定顺序 函数调用时,复合赋值操作是单次求值。

第 7 段:E1 op = E2 形式的表达式的行为等同于 E1 = E1 op E2,只不过 E1 是 仅评价一次。在 += 和 -= 中,E1 要么具有算术类型,要么是指向可能的 cv 限定的完全定义的对象类型。在所有其他情况下,E1 应具有算术类型。

GCC似乎正在利用这个规则进行内部改造(a += a) += a to (a = a + a) += a to a = (a = a + a) + a (since a = a + a必须仅评估一次) - 并且对于此表达式,正确应用了排序规则。

然而,Clang 似乎以不同的方式执行最后一个转换步骤:auto temp = a + a; temp = temp + a; a = temp;

不过,两个编译器都会对此发出警告(来自原始代码):

  • GCC: warning: operation on 'abgr' may be undefined [-Wsequence-point]

  • clang: warning: unsequenced modification and access to 'abgr' [-Wunsequenced]

因此,编译器编写者知道这种歧义,并决定以不同的方式确定优先级(GCC:第 7 段 > 第 1 段;clang:第 1 段 > 第 7 段)。

这似乎是标准中的一个缺陷。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++17 赋值排序:GCC 中仍未实现? 的相关文章

随机推荐