[2026] C++ Initialization Order: Static Fiasco, Members, and TU Rules

[2026] C++ Initialization Order: Static Fiasco, Members, and TU Rules

이 글의 핵심

C++ initialization phases: zero, constant, dynamic; per-TU vs cross-TU order; member order vs initializer list; static initialization order fiasco; Meyers singleton and constinit.

What is initialization order—and why it matters

Problem: Global y might be initialized using global x from another translation unit—cross-TU order is unspecified. file1.cpp:

int compute() { return 100; }
int x = compute();  // dynamic initialization

file2.cpp:

extern int x;
int y = x * 2;  // x might not be initialized yet!

This is the static initialization order fiasco. 아래 코드는 mermaid를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

flowchart TD
    subgraph file1[file1.cpp]
        x["int x = compute()"]
    end
    subgraph file2[file2.cpp]
        y["int y = x * 2"]
    end
    subgraph order[initialization order]
        q["which runs first?"]
        a["unspecified across TUs"]
    end
    x --> q
    y --> q
    q --> a

Table of contents

  1. Initialization phases
  2. Order within one TU
  3. Order across TUs
  4. Member initialization order
  5. Static initialization order fiasco
  6. Common mistakes
  7. Production patterns
  8. Full example

1. Initialization phases

Three stages

아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

static int a;           // zero initialization
constexpr int b = 10;   // constant initialization
constinit int c = 20;   // C++20: compile-time check
int func() { return 30; }
int d = func();         // dynamic initialization
// Conceptually: zero -> constant -> dynamic
PhaseWhenExample
ZeroBefore dynamic initstatic int x; → 0
ConstantCompile timeconstexpr int x = 10;
DynamicRuntimeint x = f();

2. Order within one translation unit

Top-to-bottom declaration order for namespace-scope objects in that TU.

int a = 10;
int b = a * 2;  // a first -> b = 20
int c = b + a;  // b,a first -> c = 30

3. Order across translation units

Unspecified for dynamic initialization of namespace-scope objects in different TUs. file1.cpp — global Logger
file2.cpp — global Database using Logger in its constructor If Database initializes before Logger, you may use Logger before its constructor runs—UB.

4. Member initialization order

Declaration order wins, not the order in the ctor initializer list. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

struct Data {
    int b;
    int a;
    
    Data() : a(10), b(a * 2) {
        // actual order: b, then a (declaration order)
        // b = a*2 runs while a is still uninitialized
    }
};

Fix: declare a before b if b depends on a.

Base → members → ctor body

struct Base {
    Base() { std::cout << "1. Base\n"; }
};
struct Member {
    Member() { std::cout << "2. Member\n"; }
};
struct Derived : Base {
    Member m;
    Derived() {
        std::cout << "3. Derived\n";
    }
};
// Output: 1 Base, 2 Member, 3 Derived

5. Static initialization order fiasco

Problem

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// file1
int x = 100;
// file2
extern int x;
int y = x * 2;  // x might still be 0

Fix 1: Meyers singleton (function-local static)

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Logger {
public:
    static Logger& instance() {
        static Logger logger;  // initialized on first use (C++11 thread-safe)
        return logger;
    }
    void log(const char* msg) { /*...*/ }
private:
    Logger() {}
};
class Database {
public:
    Database() {
        Logger::instance().log("Database created");
    }
};

Fix 2: constinit (C++20)

constinit int x = 100;
extern constinit int x;
constinit int y = x * 2;  // both constant-init compatible

Fix 3: Lazy init via function

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

int& get_x() {
    static int x = 100;
    return x;
}
int& get_y() {
    static int y = get_x() * 2;
    return y;
}

6. Common mistakes

Member order mismatch

Match declaration order and initializer list.

Cross-TU globals

Avoid non-constant dependencies; prefer function access or constinit where applicable.

Static destruction order

Destructors of globals run in reverse order of initialization—avoid touching another global’s state in a destructor unless order is guaranteed (usually you cannot).

7. Production patterns

Meyers singleton

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class ResourceManager {
public:
    static ResourceManager& instance() {
        static ResourceManager mgr;
        return mgr;
    }
    void load() {}
private:
    ResourceManager() {}
    ResourceManager(const ResourceManager&) = delete;
    ResourceManager& operator=(const ResourceManager&) = delete;
};

constinit constants

constinit int MAX_CONNECTIONS = 1000;

Nifty counter (legacy I/O streams style)

Sometimes used for libraries that must initialize exactly once across TUs—prefer simpler patterns in new code.

8. Example: safer globals

See the Korean article’s ResourceManager / Database / Cache sample—pattern: first use initializes the manager; globals register during dynamic init but only touch the singleton through instance().

Rules summary

ScopeOrder
Within one TUDeclaration order
Across TUsUnspecified for dynamic init
MembersDeclaration order (not ctor list order)
Base / members / bodyBase → members → constructor body
DestructionReverse of initialization

FAQ

Q1: Cross-TU init order?

A: Unspecified for dynamic initialization—do not depend on it.

Q2: What is the static initialization order fiasco?

A: Globals in different TUs can observe each other half-initialized if you depend on order.

Q3: Fixes?

A: Function-local statics, constinit, lazy initialization.

Q4: Member order?

A: Declaration order in the class—must match dependencies.

Q5: What is constinit?

A: C++20—variable must have static initialization; cannot be initialized by a non-constexpr runtime call.

Q6: Resources?

A: cppreference — initialization, Effective C++ Item 4, C++ Primer.

Practical tips

Debugging

  • Warnings first

Performance

  • Profile

Code review

  • Conventions

Practical checklist

Before coding

  • Right approach?
  • Maintainable?
  • Performance?

While coding

  • Warnings?
  • Edge cases?
  • Errors?

At review

  • Intent?
  • Tests?
  • Docs?

Keywords

C++, initialization order, static initialization, fiasco, constinit

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3