[[no_unique_address]] since C++20

  • [[no_unique_address]] applies to user-defined types (e.g., empty or stateless classes or structs).
  • It does not apply to fundamental types (int, float, etc.), as they always require memory for storage.
  • The attribute optimizes memory layout by allowing empty or stateless user-defined types to overlap memory locations, improving efficiency without violating the C++ object model.

Motivation

Prior to C++20, Empty Base Optimization (EBO) allowed an empty base class to take zero space when it was inherited by another class. However:

  1. Empty non-static data members were not optimized similarly. They still occupied space for alignment or padding.
  2. Non-empty types could not benefit from the same optimizations, even if they were logically redundant or empty.

[[no_unique_address]] explicitly instructs the compiler that the programmer allows empty data members to share addresses with other members or base classes, eliminating padding and reducing the size of objects.

Usage

By marking a non-static data member with [[no_unique_address]], the compiler is allowed to optimize its placement. If the member is an empty type or stateless, the compiler can place it at the same memory location as other data members (if it doesn’t violate the object model).

Code Example

#include <iostream>

struct Empty {};

struct RegularPair {
    Empty first;
    int second;
};

struct OptimizedPair {
    [[no_unique_address]] Empty first; // optimized away
    int second;
};


int main() {
    std::cout << "Size of RegularPair: " << sizeof(RegularPair) << " bytes\n";  // 8 bytes with padding
    std::cout << "Size of OptimizedPair: " << sizeof(OptimizedPair) << " bytes\n"; // 4 bytes
    
    
    return 0;
}