首页 > 解决方案 > 函数返回值的位运算符提升

问题描述

带代码:

#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

标签: c++gccbit-manipulationoperators

解决方案


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

x |= y

相当于

x = x | y

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

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

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

也不会在 GCC 中产生警告。但是只要在表达式的右侧有一个函数调用,即使函数调用的结果根本没有被使用,例如,在

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

出现警告因此,我会得出结论,这是 GCC 中的一个错误,如果您能写一份错误报告,那就太好了。…


推荐阅读