5

The test code:

struct A
{
    uint32_t lo : 16;
    uint32_t hi : 16;
};

int main()
{
    A a{};
    a.lo = 0xFFFF;
    auto b = a.lo << 16;
    cout << b << endl;
    return 0;
}

The output is:-65536, and the type of b is int but not uint32_t.

I have found that, uint16_t and uint8_t will also become signed int after shift operator, and there was a similar question in C#, which came to the conclusion that the result would become signed when the operand is <32 bits. Why do shift operations always result in a signed int when operand is <32 bits

But the type of a.lo is clearly uint32_t, which can be verified by decltype(a.lo), so how can this be explained?

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
xxhxx
  • 871
  • 5
  • 11
  • Another question, an unsigned int of bit-field, can it be used as a normal unsigned int safely? – xxhxx Dec 15 '20 at 12:52
  • Just for the record: Shifting an integer is only defined for offsets less than its width. You're shifting a 16 bit value by 16 bits. If it was a plain `uint16_t`, I would say it's UB, but to be honest, I'm not sure about bitfields. You might want to adjust that in your code though, just to not distract from the actual question. – Ulrich Eckhardt Dec 15 '20 at 12:58
  • @UlrichEckhardt - It's a 16 bit value, but the shifted operand is not a 16 bit integer. It's promoted. – StoryTeller - Unslander Monica Dec 15 '20 at 13:03
  • Btw MSVC doesn't do this. –  Dec 15 '20 at 13:07
  • @xxhxx If you would have `a.lo = 0xF000; auto b = a.lo >> 1;ˋ what you would expect as a result? And type? And if it was signed, would you expect bit field to be negative? I don’t knows all rules but different peoples might have different expectation. At most one rule could be the right one or it might also be not specified... If the width is exactly 16, why use a bit field? – Phil1970 Dec 15 '20 at 13:10
  • @Phil1970 I want to treat `a.lo` as a normal unsigned integer, that's why I declare it as `uint32_t`. – xxhxx Dec 15 '20 at 13:14
  • 1
    Why don't you just use `uint16_t` if you want a normal unsigned integer with 16 bits? It won't help with your integral promotion problem, but it's more straightforward than using a bit field with the same width as an existing primitive... – Useless Dec 15 '20 at 14:16

1 Answers1

7

It's part of the standard integral promotion.

[expr.shift]

1 The shift operators << and >> group left-to-right

The operands shall be of integral or unscoped enumeration type and integral promotions are performed. The type of the result is that of the promoted left operand.

[conv.prom]

5 A prvalue for an integral bit-field ([class.bit]) can be converted to a prvalue of type int if int can represent all the values of the bit-field; otherwise, it can be converted to unsigned int if unsigned int can represent all the values of the bit-field. If the bit-field is larger yet, no integral promotion applies to it. If the bit-field has an enumerated type, it is treated as any other value of that type for promotion purposes.

Promotion of your left operand (the bit-field) produces an int, and so that's the type of the entire shift expression. Thus b is an int too by placeholder type deduction.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • There's one thing I still don't understand, during the shift operation, the declaration `uint32_t` for `a.lo` seems didn't work, but on the contrary, `decltype(a.lo)` would result in `uint32_t`. – xxhxx Dec 15 '20 at 13:20
  • 1
    @xxhxx - `decltype` follows a different set of rules. Applied to a member, it tells you its declared type. – StoryTeller - Unslander Monica Dec 15 '20 at 13:25