5. ODR in Headers
In a latency-critical HFT codebase, reproducible builds and fast deploys depend on clean headers and link hygiene. A subtle ODR mistake can compile fine per TU but explode at link time—or worse, slip into UB across DSOs. Diagnose and fix the following to maintain deterministic, low-latency builds.
// h.hpp
int g = 0;
int inc(){ return ++g; }
// a.cpp
int f(){ return inc(); }
// b.cpp
int h(){ return inc(); }
Part 1.
What goes wrong in the compile→link pipeline, and why? Propose ODR-safe fixes that preserve a single shared counter across the program.
Part 2.
(1) Why does this violate the ODR during linking?
(2) Two ODR-safe fixes that preserve one global state?
(3) When should functions in headers be inline? Pitfalls?
(4) Compare static versus inline for header globals, including ABI effects.
(5) Do include guards prevent ODR violations across translation units? Explain.
Answer
Answer (Part 1)
The header defines non-inline entities with external linkage, so each TU emits strong definitions of g and inc, causing duplicate symbols at link. Fix either by moving definitions to a single .cpp and putting only declarations in the header, or by using C++17 inline variable/function with identical definitions.
Example (one-definition model):
// h.hpp: extern int g; int inc();
// h.cpp: int g = 0; int inc(){ return ++g; }
Answer (Part 2)
(1) Each TU includes the header, emitting separate strong definitions of g and inc. The linker sees multiple definitions, violating the ODR.
(2) Use extern int g; int inc(); in the header and define both in one .cpp. Or use inline int g = 0; inline int inc(){ return ++g; } (C++17+).
(3) Inline header functions when header-only or tiny hot-path functions require visible definitions. Pitfalls include ODR mismatches, code bloat, and tighter ABI coupling.
(4) static gives internal linkage per TU, creating separate counters and changing semantics. inline preserves one program-wide entity while allowing header definitions, maintaining intended ABI.
(5) Include guards stop multiple inclusion in one TU only. They do not prevent multiple TUs from defining the same symbols, so ODR violations remain.