函数返回值的位运算符提升

时间:2019-04-11 18:01:46

标签: c++ gcc bit-manipulation operators

使用代码:

#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()中,我首先将其分配给变量,然后没有警告。

(只有当c ++时,clang才会发出警告)

  • 这可以通过强制转换而不是变量分配来解决吗?
  • 为什么在使用函数时行为会有所不同?

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

1 个答案:

答案 0 :(得分:2)

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

x |= y

等同于

x = x | y

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

我看到的第一个版本和第二个版本之间的唯一区别是d是一个右值,而c = c | a()是一个左值。但是,值类别不应影响常规算术转换的行为。因此,警告(无论是否必要)至少应始终如一地发出。如您所知,其他编译器(例如clang)在此处不会发出警告。此外,这个问题似乎是函数调用涉及复合分配的一个奇怪的特定问题。正如SergeyA在上面的另一条评论中所指出的那样,GCC将不会以c |= static_cast<std::uint8_t>(42); 的等效形式生成警告。使用其他种类的右值代替函数调用,例如,转换文字的结果

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

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

create table #temp
    ( STUDENT_KEY INT identity (10000000,1),
    STUDENT_ID  AS CONCAT('STD',STUDENT_KEY),
    STUDENT_FIRSTNAME VARCHAR(255), 
    STUDENT_SURNAME VARCHAR(255)
    )

INSERT INTO #temp
VALUES  ( 'Daenerys' , 'Targaryen' ),
        ( 'Jon' , 'Snow' ),
        ( 'Gregor' , 'Clegane' ),
        ( 'Arya' , 'Stark' ),
        ( 'Cersei' , 'Lannister' ),
        ( 'Joffrey' , 'Baratheon' ),
        ( 'Petyr' , 'Baelish' ),
        ( 'Khal' , 'Drogo' ),
        ( 'Theon' , 'Greyjoy' ),
        ( 'Ramsey' , 'Bolton' )


SELECT * FROM #temp

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