【问题标题】:bit operator promotion on function return value函数返回值的位运算符提升
【发布时间】:2019-04-11 18:01:45
【问题描述】:

带代码:

#include <cstdint>

uint8_t a() { return 5; }

auto b() {
    uint8_t c = 6;
    c |= a();  // Warning here
    return c;
}

auto d() {
    uint8_t c = 6;
    uint8_t d = a();
    c |= d;
    return c;
}

g++ 警告(使用 -Wconversion):

<source>:7:12: warning: conversion from 'int' to 'uint8_t' {aka 'unsigned char'} may change value [-Wconversion]

我假设问题与位操作的整数提升有关,但在第二个函数 d() 中,我首先将它分配给一个变量,然后没有警告。

(clang 不会对此发出警告,只有 g++)

  • 可以通过强制转换而不是变量赋值来解决这个问题吗?
  • 为什么我使用函数时它的行为会有所不同?

具有上述内容的编译器资源管理器:https://godbolt.org/z/q9eMVT

【问题讨论】:

  • 对我来说这看起来像是一个 gcc 错误 - 警告是虚假的。当该行被重写为语义等效的 c = c | a; 时,相同的 gcc 不会发出警告。
  • hmm 没有警告 here 使用 gcc 8.3
  • 啊,没有警告,因为我忘记了-Wconversion

标签: c++ gcc bit-manipulation operators


【解决方案1】:

我认为这是 GCC 中的一个错误。基于[expr.ass]/7,表达式

x |= y

等价于

x = x | y

除了a 只被评估一次。在按位包含 OR 中,与其他按位运算一样,也正如上面 CoryKramer 的评论中所指出的,通常的算术转换将首先在两个操作数 [expr.or]/1 上执行。由于我们的两个操作数都是std::uint8_t 类型,因此通常的算术转换只是整数提升[expr.arith.conv]/1.5std::uint8_t 上的积分提升应该意味着两个操作数都转换为 int [conv.prom]/1。不需要进一步转换操作数,因为两个操作数的转换类型相同。最后,由| 表达式产生的int 然后被转换回std::uint8_t 并存储在x [expr.ass]/3 引用的对象中。我会假设这最后一步是在某些情况下触发警告的原因。但是,无论我们是否转换为intis guaranteed to be larger)并沿途返回,两个std::uint8_t 之间的按位逻辑或结果不可能在std::uint8_t 中表示.因此,这里不需要警告,这可能是通常不产生警告的原因。

我看到的第一个版本和第二个版本之间的唯一区别是 a() 是右值,而 d 是左值。但是,值类别不应该对通常的算术转换的行为产生影响。因此,警告——无论是否不必要——至少应该始终如一地产生。正如您自己所注意到的,其他编译器(例如 clang)不会在此处产生警告。此外,这个问题似乎奇怪地特定于函数调用在复合赋值中的参与。正如 SergeyA 在上面的另一条评论中所指出的,GCC 不会以 c = c | a() 的等效形式产生警告。使用其他类型的右值代替函数调用,例如,转换文字的结果

c |= static_cast<std::uint8_t>(42);

will also not produce a warning in GCC。但是只要表达式的右边有一个函数调用,即使函数调用的结果根本没有被使用,例如,在

c |= (a(), static_cast<std::uint8_t>(5));

the warning appears。因此,我会得出结论,这是 GCC 中的一个错误,如果您愿意 write a bug report,那就太好了。 …

【讨论】:

    猜你喜欢
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 2022-03-19
    • 1970-01-01
    • 2016-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多