Welcome to the next pikoTutorial!
The error we’re handling today is a C++ linker error:
undefined reference to `X`
collect2: error: ld returned 1 exit statusWhat does it mean?
In C++, an “undefined reference” error occurs during the linking stage. This error indicates that the linker cannot find the definition for a function, variable or object that has been declared but not defined. It typically happens when the code is separated into multiple files, and the linker cannot locate the necessary object files or libraries where the symbol is defined.
How to fix it?
Missing definition
Look at this code:
void function();
int main()
{
function();
}The function has been declared, but there’s no definition, so the linker will complaining throwing the following error:
main.cpp:(.text+0x9): undefined reference to `function()'
collect2: error: ld returned 1 exit statusTo fix this, we need to add the implementation of the function:
void function()
{
// implementation
}
int main()
{
function();
}Now everything works as expected.

AI is powerful. Snippets are instant.
Stop prompting for the same patterns repeatedly. Get almost 100 free VS Code snippets for C++, Python, CMake and Bazel from piko::snippets GitHub repository.
Declaration/definition mismatch
Look at these 3 files:
function.h
void function(int);function.cpp
#include "function.h"
void function()
{
// implementation
}main.cpp
#include "function.h"
int main()
{
function(2);
}In this case we get an error:
main.cpp:(.text+0xe): undefined reference to `function(int)'
collect2: error: ld returned 1 exit statusLinker tells us that it can’t find definition of function(int) and indeed, when we look closer at the function definition in .cpp file we see that although it has the same return type and the same name, it has no input argument, so the linker treats it as a completely different function than the one which has been declared in .h file. After adding the int input argument in the function’s definition, everything works fine:
void function(int)
{
// implementation
}File not added to the compilation
Look at these 4 files:
function.h
void function(int);function.cpp
#include "function.h"
void function(int)
{
// implementation
}main.cpp
#include "function.h"
int main()
{
function(2);
}CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyProject)
add_executable(MyExec main.cpp)Here, although the function definition is present and there is no declaration/definition mismatch, we still get the same linker error. The reason is visible in the last line of CMakeLists.txt file. We’ve included in the compilation only source file (main.cpp) out of 2 that we have (main.cpp and function.cpp). So despite the function definition being present in the source code, it is not included in the compilation process. To fix this, we need to add function.cpp file in the CMakeLists.txt file:
cmake_minimum_required(VERSION 3.15)
project(MyProject)
add_executable(MyExec main.cpp function.cpp)Library with the function definition not linked
Now let’s assume that we have the same set of files as in the previous example, but with one change – this time function() is defined and declared in a separate library:
cmake_minimum_required(VERSION 3.15)
project(MyProject)
add_library(FunctionLib function.cpp)
add_executable(MyExec main.cpp)Despite the fact that the function definition exists, is correct and even function.cpp file is successfully compiled into a library, the whole process fails because the linker still complains about the undefined reference to function(int). The reason is that although FunctionLib has been successfully compiled, it has not been linked to our executable MyExec. After linking everything works as expected:
cmake_minimum_required(VERSION 3.15)
project(MyProject)
add_library(FunctionLib function.cpp)
add_executable(MyExec main.cpp)
target_link_libraries(MyExec PRIVATE FunctionLib)Read also:
- When AI gets C++ bitmask enums almost right
- 10 VS Code snippets essential for every C++ engineer
- Storing your ATTiny program in…EEPROM?
- Bug of the week #12
- Sharing variable between bash scripts
- A 40-line LLM-based bash command executor in Python
- GTest and short-circuit evaluation in C++
- AI is powerful. Snippets are instant.
- From AUTOSAR to S-Core: the first C++ pub/sub implementation
- How to write Arduino Uno code with Python?
- Combining Bazel with Docker
- Running commands with timeout on Linux
- Running Python unit tests with CMake
- Thirdparty dependencies with FetchContent
- Bug of the week #11
- Combining CMake with Docker
- How to search the internet from Linux terminal?
- Folding expressions in C++
- How to derive from an enum in Python?
- Bug of the week #10
- Trying ROS2: client/server within a single container
- Make C++ a better place #4: Go as an alternative
- How to convert hex to dec in Linux terminal?
- Setting up a Python project with CMake
- Separating builds for different configs with Bazel
- Trying ROS2: pub/sub within a single container
- Bug of the week #9
- UDP multicasting with Python
- Destruction order vs thread safety in C++
- Let’s review some code: C++ #2
- Make C++ a better place #3: D as an alternative
- Registering callback using std::function in C++
- Bug of the week #8
- TCP client/server with Python
- Simple menus in Bash scripts with select
- Calling member function on a nullptr in C++
- Bug of the week #7
- Python lru_cache explained
- How to dockerize a Python application?
- Make C++ a better place #2: CppFront as an alternative
- Parameters combinations in GoogleTest
- Data transfer with curl
- Python reduce explained
- Bug of the week #6
- Custom literals in C++
- Linux and hash command
- 5 Python good practices which make life easier
- Let’s review some code: Python #1
- Make C++ a better place #1: What does better mean
- Enums vs enum class in C++
- Bug of the week #5
- UDP client/server with Python
- Hard links in Linux
- Functions calling order in unit tests in C++
- Bug of the week #4
- Yield in Python – state machines, coroutines and more
- Copy files from another branch with Git
- Make C++ a better place #0: Introduction
- 5 misconceptions about std::move in C++
- How to use xargs on Linux?









