Compiling a C++ program takes place in several steps i.e.
- Physical source file characters are mapped, in an implementation-defined manner, to the basic source character set (introducing new-line characters for end-of-line indicators) if necessary.
- The source file is decomposed into preprocessing tokens and sequences of white-space characters (including comments). Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed.
- White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. The resulting tokens are syntactically and semantically analyzed and translated as a translation unit.
- All external entity references are resolved. Library components are linked to satisfy external references to entities not defined in the current translation. All such translator output is collected into a program image which contains information needed for execution in its execution environment.
“Undefined reference/unresolved external symbol” errors occur during this last stage of compilation, most commonly referred to as linking. Say you defined symbol
b.cpp declared that symbol and used it. Before linking, it simply assumes that that symbol was defined somewhere, but it doesn’t yet care where. The linking phase is responsible for finding the symbol and correctly linking it to b.cpp (in general to the object or library that uses it).
If you’re using Microsoft Visual Studio, you’ll see that projects generate .lib files. These contain a table of exported symbols, and a table of imported symbols. The imported symbols are resolved against the libraries you link against, and the exported symbols are provided for the libraries that use that .lib (if any).