3. Static in Headers
In HFT, hot code often lives in headers to enable cross-TU inlining and LTO. Misusing static in headers can silently duplicate state, alter linkage, and inflate init/teardown costs. Understand the storage duration vs. linkage consequences to avoid nondeterministic behavior.
static int hits = 0;
inline int& H() { return hits; }
struct Book { int x; };
static Book def{7};
extern int g;
Part 1.
Assuming this snippet is in a header included by two .cpp files, identify the storage duration and linkage of hits, H, Book, def, and g. How many distinct runtime instances or definitions exist for each at link-time?
Part 2.
(1) What fails if inline is removed from H() when defined in a header?
(2) Contrast static storage duration and internal linkage.
(3) Safer alternative to per-TU static int hits shared across TUs?
(4) Consequences of static object with non-trivial destructor in headers?
(5) When should extern declarations appear in headers versus definitions in a single .cpp?
Answer
Answer (Part 1)
hits: static storage duration, internal linkage; one object per TU (two total). H: external linkage but inline; multiple identical definitions allowed, each returning a reference to its TU’s hits.
Book: external linkage type; one type across the program. def: static storage duration, internal linkage; one object per TU (two total). g: external linkage declaration only; zero storage here, must have exactly one definition elsewhere (static storage duration) or ODR/link error.
Answer (Part 2)
(1) You get multiple external definitions of H across TUs, violating the ODR. Typically this triggers a link-time multiple-definition error.
(2) Static storage duration governs lifetime (entire program). Internal linkage restricts symbol visibility to the defining TU; they are orthogonal properties.
(3) Use a C++17 inline variable (inline int hits = 0;) or extern int hits; in the header with one definition in a .cpp. Both yield a single shared object across TUs.
(4) Each TU gets its own instance and destructor, increasing exit-time work. It also risks static initialization/termination order issues and longer startup latency.
(5) Put extern declarations in headers to share a name, and place exactly one definition in a .cpp. This prevents multiple definitions while keeping a single shared object.