Program Build — three tools, three error models
When you press Build in an IDE or run g++, it looks like your source file becomes a program instantly. In reality it passes through a chain of three fundamentally different tools — preprocessor, compiler, linker — and each one speaks its own error language. Most "mysterious" messages (undefined reference, multiple definition, use of undeclared identifier) stop being mysterious the moment you know which stage produced them.
The abstraction underneath everything is the translation unit (TU): one .cpp file after preprocessing, together with every header it included. The compiler sees one TU at a time and knows nothing about the rest of the project — that is why #include literally pastes text, and why names must be either declared in every TU or resolved later by the linker. The ODR, linkage rules, inline, file-scope static, and the categories of build errors all flow from that.
C++ is also unusual in how much of the program runs before launch, at compile time: constexpr, consteval, constinit, static_assert, templates. That delivers zero runtime cost — but only if you understand where each computation actually happens. The full picture lives in the layers below.
Topic map
- Preprocessor —
#include,#define, conditional compilation, header guards /#pragma once, why#include <iostream>slows builds down. - Compiler — compilation stages, key GCC/Clang/MSVC flags, diagnostics, optimization levels, ABI.
- Linker — static vs dynamic linking, symbol resolution, ODR,
undefined referencevsmultiple definition. - Visibility and linkage — scope, linkage (internal/external/none), file-scope
static,extern, anonymous namespaces. - Compile-time computation —
constexpr,consteval,constinit,static_assert, template computation and where the work happens.
Common traps
| Mistake | Stage | Cause |
|---|---|---|
undefined reference to 'foo' | Linker | Declaration exists but no definition was linked (missing .o or library) |
multiple definition of 'foo' | Linker | ODR violation — a definition in a header without inline reached multiple TUs |
use of undeclared identifier | Compiler | Name not declared in this TU — missing #include |
error: 'foo' redefined | Compiler | Header included twice without include guards / #pragma once |
Non-static global variable in a header | Linker | multiple definition — needs inline (C++17) or extern + a single definition in .cpp |
static_assert in a template not depending on its parameter | Compiler | Fires unconditionally, not only on instantiation |
consteval call with a runtime argument | Compiler | consteval must run at compile time |
| Relying on global initialization order across TUs | Runtime | Static initialization order fiasco — order is unspecified |
#include <bigheader> in a frequently-included header | Compiler | Every TU reparses megabytes of code — slow builds |
| Mixing builds with different ABIs / stdlibs | Linker/Runtime | std::string, exceptions, RTTI break between object files |
Interview relevance
Program build is a foundational topic asked of juniors and regularly revisited for middle/senior in the context of large projects and templates. The interviewer is not checking memorized flags — they are checking your ability to explain "why is it built this way."
Typical checks:
- The difference between a compiler error and a linker error — and how to tell them apart from the message text.
- What a translation unit is and why separate compilation matters.
- The one-definition rule (ODR) — where a definition is allowed and where it is not.
- Why a function in a header needs
inlinebut a class does not. - Static vs dynamic library.
- What
staticmeans at file scope vs inside a class vs inside a function. - What
extern "C"does and when you need it. - Where the work happens: compile-time vs runtime;
constexprvsconstevalvsconstinit.
Common wrong answer: "undefined reference means I forgot to #include something." That is exactly the opening to explain the difference between a declaration (which fixes use of undeclared identifier at compile time) and a definition (which must be linked — undefined reference comes from the linker).