C++ EBCO and [[no_unique_address]] | 'Empty Base Optimization' Complete Guide

C++ EBCO and [[no_unique_address]] | 'Empty Base Optimization' Complete Guide

이 글의 핵심

EBCO and C++20 [[no_unique_address]] solving the problem of empty classes occupying memory. From std::tuple, std::unique_ptr implementation secrets, memory layout optimization to practical patterns.

Introduction: “Why does empty class occupy 1 byte?"

"Why is std::unique_ptr<T, Deleter> 16 bytes instead of 8 bytes?”

In C++, empty class (Empty Class—class without member variables) also occupies minimum 1 byte. This is because each object must have unique address. But this rule causes wasted memory when having stateless functors or custom deleters as members.

EBCO (Empty Base Class Optimization) is a compiler optimization where inheriting empty class as base makes size 0. C++20’s [[no_unique_address]] attribute allows applying this optimization to member variables too.

What This Guide Covers:

  • Empty class rule: Why it occupies 1 byte
  • EBCO principle: Size 0 when inherited as base class
  • [[no_unique_address]]: C++20 member variable optimization
  • Practical usage: std::tuple, std::unique_ptr, compressed pair
  • Problem scenarios: When memory layout optimization is needed
  • Complete examples: Custom smart pointer, compressed pair implementation
  • Common errors: EBCO failure cases, ABI compatibility
  • Production patterns: Standard library implementation techniques

1. Empty Class Rule and EBCO

Why Empty Class Occupies 1 Byte

C++ standard requires all objects have unique address. Even empty class needs minimum 1 byte for each array element to have different address.

Below is an implementation example using C++. Define a class to encapsulate data and functionality. Understand the role of each part while examining the code.

struct Empty {};

int main() {
    Empty e1, e2;
    std::cout << sizeof(Empty) << '\n';  // 1
    std::cout << &e1 << " vs " << &e2 << '\n';  // Different addresses
    
    Empty arr[10];
    std::cout << &arr[0] << " vs " << &arr[1] << '\n';  // 1 byte apart
}

Output:

1
0x7ffc1234 vs 0x7ffc1235
0x7ffc1240 vs 0x7ffc1241

Problem When Having as Member

Below is an implementation example using C++. Define a class to encapsulate data and functionality. Try running the code directly to check its operation.

struct Empty {};

struct Container {
    int value;      // 4 bytes
    Empty empty;    // 1 byte + 3 bytes padding (due to alignment)
    // Total 8 bytes
};

static_assert(sizeof(Container) == 8);

Problem: Empty has no state but wastes 4 bytes (including padding).

EBCO: Size 0 When Inheriting as Base Class

EBCO (Empty Base Class Optimization) is compiler optimization making empty base class size 0.

Below is an implementation example using C++. Define a class to encapsulate data and functionality. Try running the code directly to check its operation.

// Type definition
struct Empty {};

struct Optimized : Empty {
    int value;  // 4 bytes
    // Empty is size 0 → Total 4 bytes
};

static_assert(sizeof(Optimized) == 4);

Key: Base class is not array element so unique address rule is relaxed. Compiler places empty base at same address as derived class start to save space.


2. C++20 [[no_unique_address]]

Basic Usage

C++20 [[no_unique_address]] attribute allows applying EBCO to member variables.

struct Empty {};

struct Optimized {
    int value;                          // 4 bytes
    [[no_unique_address]] Empty empty;  // 0 bytes
    // Total 4 bytes
};

static_assert(sizeof(Optimized) == 4);

Comparison with EBCO

MethodSyntaxLimitation
EBCOInherit as baseInheritance required
[[no_unique_address]]Member attributeC++20 required

Summary

Key Points

  1. Empty class: Occupies minimum 1 byte
  2. EBCO: Size 0 when inherited as base class
  3. [[no_unique_address]]: C++20 member optimization
  4. Use cases: Smart pointers, tuple, compressed pair
  5. Conditions: Empty type, no virtual functions

When to Use

Use EBCO/[[no_unique_address]] when:

  • Member is empty type
  • Want to minimize memory
  • Implementing smart pointers/containers
  • Optimizing memory layout

Don’t use when:

  • Type has state
  • Has virtual functions
  • Adds unnecessary complexity

Best Practices

  • ✅ Use for stateless functors
  • ✅ Apply in custom smart pointers
  • ✅ Use [[no_unique_address]] in C++20
  • ❌ Don’t assume EBCO always applies
  • ❌ Don’t forget ABI compatibility

  • C++ Alignment and Padding
  • C++ Cache and Data-Oriented Design
  • C++ Pimpl

Master EBCO for memory-efficient C++ code! 🚀