LNK2019 Unresolved External Symbol in C++: Five Causes and Fixes (Visual Studio & CMake)
이 글의 핵심
Linker LNK2019 means the linker cannot find a symbol’s definition. This guide walks compile vs link, five root causes, and how to verify fixes in Visual Studio and CMake.
Modern CMake: targets & link libraries · generic CMake failures: common CMake errors.
Introduction: the linker error every C++ developer hits
LNK2019: unresolved external symbol is a linker failure: the compiler accepted your translation units, but when linking object files and libraries into an executable or DLL, the linker could not find a definition for a referenced symbol.
Roughly: “there is a declaration, but no matching definition reached the link step.”
This article covers:
- Compile vs link (why the error appears at link time)
- Five major causes and fixes
- Visual Studio and CMake checks
Environments: Examples use Visual Studio on Windows and GCC/Clang + CMake on Linux/macOS. The ideas transfer to any toolchain once you map them to your IDE.
Table of contents
- What is LNK2019? (compile vs link)
- Cause 1: declaration without definition
- Cause 2:
.cppnot in the build - Cause 3: library not linked
- Cause 4: namespace / name mismatch
- Cause 5: template definition not in header
- Checking in Visual Studio
- Checking in CMake
1. What is LNK2019?
Compile vs link
1. Compile turns each .cpp into an object file (.obj / .o). The compiler only needs declarations to type-check calls—e.g. void foo(); is enough to compile a call site.
2. Link merges objects and libraries into the final binary. The linker needs a unique definition for every referenced symbol. If none is found → LNK2019.
Sample message
error LNK2019: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
referenced in function _main
Meaning: foo is used from main, but no object pulled into the link defines foo.
Why two phases?
Incremental builds: change one .cpp, recompile it, relink—faster than compiling everything every time.
flowchart LR
subgraph compile["Step 1: compile"]
A[.cpp + headers] --> B[Compiler]
B --> C[.obj / .o]
C --> D["Declaration-only can compile"]
end
subgraph link["Step 2: link"]
C --> E[Linker]
E --> F[.exe]
E -.->|"no definition"| G[LNK2019]
end
2. Cause 1: declaration but no definition
utils.h
#ifndef UTILS_H
#define UTILS_H
void printMessage(); // declaration only
#endif
main.cpp
#include "utils.h"
int main() {
printMessage(); // LNK2019 if no TU defines printMessage
return 0;
}
Fix: add utils.cpp (or define in a TU that is linked):
#include "utils.h"
#include <iostream>
void printMessage() {
std::cout << "Hello from utils!\n";
}
Build: ensure utils.obj participates in the link (VS: add file to project; CMake: list in add_executable or link a static lib).
Direct build:
g++ main.cpp utils.cpp -o myapp
3. Cause 2: .cpp omitted from the build
add_executable(myapp
main.cpp
# forgot utils.cpp
)
Symptom: build log never compiles utils.cpp.
Fix:
add_executable(myapp
main.cpp
utils.cpp
)
4. Cause 3: not linking the library
You included headers and compiled, but the .lib / .a (implementation) is not on the link line.
Visual Studio: Linker → Input → Additional Dependencies (ws2_32.lib, mylib.lib, …) and Additional Library Directories.
CMake:
target_link_libraries(myapp PRIVATE mylib)
CLI:
g++ main.cpp -o myapp -L/path/to/lib -lmylib
5. Cause 4: namespace / name mismatch
Declaration:
namespace util {
void printMessage();
}
Wrong definition (global):
void printMessage() { } // different symbol from util::printMessage
Fix: define inside namespace util or as void util::printMessage().
Also watch case and extern "C" for C APIs.
6. Cause 5: templates defined only in .cpp
Templates are usually instantiated where used. If the definition is hidden in a .cpp that does not see the use sites, the linker misses symbols.
Typical fix: put definitions in the header (or explicit instantiations in .cpp for known types).
// math.h
template <typename T>
T add(T a, T b) { return a + b; }
Explicit instantiation (narrow use):
template int add<int>(int, int);
template double add<double>(double, double);
7. Visual Studio checklist
- Linker → Input → Additional Dependencies
- Linker → General → Additional Library Directories
- C/C++ → General → Additional Include Directories (compile-time paths)
8. CMake checklist
add_executable(myapp main.cpp utils.cpp)
target_link_libraries(myapp PRIVATE mylib)
Debug finding:
cmake --build . --verbose
Resolution checklist
| Cause | Check | Fix |
|---|---|---|
| No definition | No TU defines the symbol | Add .cpp / library |
| Missing TU | .cpp not listed | Add to CMake/VS project |
| Missing lib | Headers only | target_link_libraries / VS libs |
| Name mismatch | Namespace / spelling | Align declaration & definition |
| Templates | Definition only in .cpp | Header definition or explicit inst. |
Practical debugging
- Read the demangled name in the error (VS shows both).
- Follow “referenced in function …” to the call site.
- Fix first LNK2019—later errors may be follow-ons.
- Match x64/x86 and Debug/Release runtimes with your
.libfiles. - For C APIs from C++, wrap includes in
extern "C"when required.
FAQ (short)
LNK2019 vs LNK1120 — per-symbol vs summary.
main unresolved — entry point / subsystem / target type.
Header-only — no .lib needed if truly header-only.
Related posts (internal)
- CMake intro
- Segfault debugging
- Compile speed (VS)
Keywords
LNK2019, unresolved external symbol, C++ linker error, target_link_libraries, Visual Studio link error
Practical tips
- Treat the first linker error as primary.
- Verify every
.cppthat defines symbols you call is compiled and linked. - For third-party SDKs, copy exact
pragma comment(lib, …)/ CMake targets from their docs.
Closing: LNK2019 means missing definition at link time. Walk definitions → object files → libraries → names → templates, and the fix is usually one configuration step away.
Search: LNK2019, unresolved external symbol, CMake link, MSVC linker