简体   繁体   中英

Bit shift leads to strange type conversion

The following code compiles without warning:

std::uint16_t a = 12;
std::uint16_t b = a & 0x003f;

However, performing a bit shift along with the bitwise and leads to an 'implicit cast warning':

std::uint16_t b = (a & 0x003f) << 10; // Warning generated.

Both gcc and clang complain that there is an implicit conversion from int to uint16_t , yet I fail to see why introducing the bit shift would cause the right hand expression to suddenly evaluate to an int .

EDIT: For clang, I compiled with the -std=c++14 -Weverything flags; for gcc, I compiled with the -std=c++14 -Wall -Wconversion flags.

Doing any arithmetic with integer types always is preceded by promotion to at least (sometimes, but not in this case using gcc, unsigned ) int . As you can see by this example , this applies to you first, warning-less variant too.

The probably best way to get around these (admittedly often surprising) integer promotion rules would be to use an unsigned int (or uint32_t on common platforms) from the get go.

If you cannot or don't want to use the bigger type, you can just static_cast the result of the whole expression back to std::uint16_t :

std::uint16_t b = static_cast<std::uint16_t>((a & 0x003f) << 10); 

This will correctly result in the RHS-value mod 2^16.

yet I fail to see why introducing the bit shift would cause the right hand expression to suddenly evaluate to an int.

I think you misinterpret the warning. In both cases expression evaluate to int but in the first case result will always fit in uint16_t , in the second case it will not. Looks like the compiler is smart enough to detect that and generates warning only in the second case.

From cppreference.com : "If the operand passed to an arithmetic operator is integral or unscoped enumeration type, then before any other action (but after lvalue-to-rvalue conversion, if applicable), the operand undergoes integral promotion ."

For instance:

byte a = 1;
byte b = a << byte(1);
  1. a and 1 are promoted to int : int(a) and int(byte(1)) .
  2. a is shifted one position to left: int result = int(a) << int(byte(1)) (the result is an int ).
  3. result is stored in b . Since int is wider than byte an warning will be issued.

If the operands are constant expressions , a compiler might be able to compute the result at compile time and issue an warning iff the result does not fit in destination:

byte b = 1 << 1; // no warning: does not exceed 8 bits
byte b = 1 << 8; // warning: exceeds 8 bits

Or, using constexpr :

constexpr byte a = 1;
byte b = a << 1; // no warning: it fits in 8 bits
byte b = a << 8; // warning: it does not fit in 8 bits

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM