Search code examples
c++bit-fields

Is this a correct way to initialize all the bit fields in a struct?


I'm packing bools together in a c++ struct and I want to initialize them all to zero when constructed.

Is this a correct/safe way to do it?

I think it is but I'm not 100% sure and I'd like the lawyers to weigh in...

struct packed_flags {
  // All the flags (there's less than 16 of them)
  bool f1: 1;
  bool f2: 1;
  bool f3: 1;
  bool f4: 1;
  // ...

  // Pad the struct to a 16-bit size
  uint16_t: 0;

  // Constructor to initialize them all to 'false'
  packed_flags() {
    *(uint16_t*)(this) = 0;
  }
} flags;

Solution

  • *(uint16_t*)(this) = 0; is an aliasing violation and therefore undefined behavior. *this is not of type uint16_t nor does any object of type uint16_t exist that is pointer-interconvertible with *this.

    The correct way to force the default constructor to zero all members is to not write any default constructor at all, instead relying on the implicitly-defined one, and explicitly specifying default member initializers instead (since C++20):

    struct packed_flags {
      // All the flags (there's less than 16 of them)
      bool f1: 1 = 0;
      bool f2: 1 = 0;
      bool f3: 1 = 0;
      bool f4: 1 = 0;
      // ...
    
      // Pad the struct to a 16-bit size
      uint16_t: 0 = 0;
    } flags;
    

    Before C++20 you can simply write a default constructor that assigns 0 to each bit-field manually.

    Or, even without these default initializers you can use initialization by () or {}, to force zero-initialization (since C++03/C++11):

    struct packed_flags {
      // All the flags (there's less than 16 of them)
      bool f1: 1;
      bool f2: 1;
      bool f3: 1;
      bool f4: 1;
      // ...
    
      // Pad the struct to a 16-bit size
      uint16_t: 0;
    } flags = {};
    

    Also, uint16_t: 0; is not guaranteed to pad the structure as you want it. It only specifies that the next bit-field shall begin at a new bit-field allocation unit boundary, but there is no following bit-field.

    If you want to force the structure to have a specific alignment (implying that its size is an integer multiple of that alignment), then you should use alignas instead:

    struct alignas(2) packed_flags {
      // All the flags (there's less than 16 of them)
      bool f1: 1;
      bool f2: 1;
      bool f3: 1;
      bool f4: 1;
      // ...
    } flags = {};