Skip to content

90 Advanced The Hundred

The Initialization Order Fiasco

two .cpp files walk into a linker…

// other.cpp
int answer() { return 42; }

int source = answer();

// main.cpp
#include <iostream>

extern int source;   // defined in other.cpp
int duplicated = source * 2;

int main() {
    std::cout << "source     = " << source << "\n";
    std::cout << "duplicated = " << duplicated << "\n";
}

Build it: g++ -std=c++17 main.cpp other.cpp -o demo. What does it print?

Answer

With GCC on x86-64, source prints 42 either way — but duplicated = 0. Swap the file names (g++ -std=c++17 other.cpp main.cpp) and the same source prints duplicated = 84.

Why

Globals within one translation unit are initialized top to bottom; across translation units, the standard leaves the relative order unspecified. Every global is zero-initialized before any dynamic initializer runs, so when main.cpp goes first, duplicated = source * 2 reads source while it is still 0 — the real 42 arrives moments too late. GCC on Linux happens to run each file's initializers in link order, which is why reordering the command line flips the answer, and why this bug can hide for years until an innocent build-system tweak. The decoy: plain int source = 42; would be constant-initialized at compile time and immune — it takes a runtime call like answer() to force dynamic initialization. This is unspecified order, not undefined behavior, and neither -Wall -Wextra nor the linker says a word.

The fix

The Meyers singleton: hide the global behind a function-local static, which is initialized on first use (thread-safely, since C++11):

// other.cpp
int& source() {
    static int value = answer();   // runs the first time anyone asks
    return value;
}

// main.cpp
int& source();
int duplicated = source() * 2;     // 84, whatever the link order

Since C++20, constinit on a global demands compile-time initialization, or the build fails.

Takeaway: never let a global's initializer read a global from another translation unit — route the access through a function-local static.

Try it: g++ -std=c++17 main.cpp other.cpp -o demo && ./demo — then swap the file names.

Open in Compiler Explorer ↗ Quiz this entry

This lesson also needs other.cpp; Compiler Explorer opens main.cpp only.