C++ / Editorial solution guide
Fix undefined references and missing template definitions
Separate a linker failure from a compiler rejection, then establish that the matching definition is compiled, visible when needed, and included in the target.
Read-only knowledge base ยท No public comments
Recognize the symptoms
A source file compiles, but the final build reports an undefined reference or an unresolved external symbol. The wording differs between toolchains. A declaration was sufficient to compile the call, but the linker cannot find the required definition. No new executable with a guaranteed result has been produced.
For a template, one argument type may work while another fails, especially when a definition was placed only in a separate implementation file. Treat the specialization named in the diagnostic as evidence rather than assuming every template link failure has the same cause.
Understand the likely causes
- An ordinary function definition may be absent from the target, have a mismatched signature or namespace, or live in a library not linked by that target. A successful compilation does not prove that the implementation object was linked. A clean build helps distinguish a real dependency defect from stale artifacts.
- Template definitions generally must be visible where the required specialization is instantiated. A declaration-only header does not generate arbitrary specializations from a definition hidden in another translation unit. Explicit instantiation is an alternative only for a deliberately supported, coordinated set of types.
Identify the exact required entity
Read the first link diagnostic and compare the required name, parameter types, const qualification, and namespace with the definition. Check the actual target sources and link command. Do not add unrelated libraries until the missing entity is understood. This declaration alone intentionally provides no implementation; it is a dependency snippet and must not be treated as a complete executable.
template<class T>
T twice(T value);
This compiles as a declaration but cannot supply every specialization by itself. It is not a standalone runnable program.
Expose the definition or instantiate deliberately
For an open-ended template interface, place the definition in its header, protected against repeated inclusion. The complete single-file harness below demonstrates the same visibility rule by putting the definition before its use. In a real project, include that defining header from each caller. If using explicit instantiation instead, keep declarations and instantiation definitions consistent and link their implementation object.
#include <iostream>
template<class T>
T twice(T value) { return value + value; }
int main() {
std::cout << twice(6) << "\n";
}
12
The int specialization can be formed because its definition is visible. Six plus six is representable and prints twelve.
Repair the target configuration
For ordinary functions, add the source or library to the target rather than including implementation source files as a workaround. Check ABI and build-mode compatibility when consuming prebuilt libraries. A duplicate-definition error after a repair is a different issue: a non-inline ordinary function may have been defined in multiple translation units. Header guards alone do not resolve that cross-unit problem.
Verify the fix
Rebuild from a fresh build directory using the same documented standard mode and dependencies. Confirm the original missing symbol is resolved and run the intended executable, not a previous artifact. The complete harness above should print 12 followed by a newline in C++20 mode.
For a template header, compile a second caller translation unit using every advertised type. This catches accidental dependence on declarations or instantiations that exist only in the original source file. Link and runtime checks answer different questions and both belong in the verification.
Keep it from recurring
- Keep target dependencies explicit, include defining template headers directly, and preserve the full build command with bug reports. Separate supported explicit instantiations from an interface that promises arbitrary compatible types.