Содержание
undefined reference to ... — ошибка компоновщика. Она означает: программа использует функцию или переменную, которую Вы объявили, но её определение не нашлось ни в одном из файлов, из которых собирается программа. Синтаксис при этом в порядке, поэтому искать опечатку в строке, на которую указывает сообщение, обычно бесполезно.
Откуда берётся ошибка
Сборка программы на C++ проходит в два шага.
- Компиляция. Каждый файл
.cppотдельно превращается в объектный файл. Компилятору достаточно объявления функции — строки видаint square(int x);. Он верит, что определение существует где-то ещё. - Компоновка. Компоновщик (linker) соединяет объектные файлы и библиотеки в одну программу и подставляет адреса функций на места их вызовов. Если определения нигде нет, подставлять нечего — возникает
undefined reference.
Поэтому ошибка появляется в самом конце сборки, уже после того, как все файлы скомпилировались без замечаний.
Как прочитать сообщение
В Linux и в MinGW на Windows сообщение выглядит так:
/usr/bin/ld: /tmp/ccAbCdEf.o: in function `main':
main.cpp:(.text+0x9): undefined reference to `square(int)'
collect2: error: ld returned 1 exit status
Главное здесь — имя в кавычках: square(int). Это то, что компоновщик искал и не нашёл, вместе с типами параметров. Строка in function main говорит, откуда функция вызывается.
В других системах та же ошибка называется иначе.
| Система | Как выглядит |
|---|---|
| Linux, MinGW | undefined reference to 'square(int)' |
| macOS | Undefined symbols for architecture arm64: "square(int)", referenced from: _main |
| Visual Studio | error LNK2019: unresolved external symbol "int __cdecl square(int)" |
Причины и способы исправления во всех трёх случаях одинаковые.
Причина 1. Функция объявлена, но не написана
#include <iostream>
int square(int x);
int main() {
std::cout << square(5) << '\n';
}
Объявление есть, тела функции нет нигде. Компилятор доволен, компоновщик — нет.
Решение: напишите определение функции.
int square(int x) {
return x * x;
}
Причина 2. Файл с определением не участвует в сборке
Это самая частая причина. Функция написана в math_utils.cpp, но собирается только main.cpp:
g++ main.cpp -o app
Компоновщик не знает о втором файле и не находит square.
Решение: передайте компилятору все файлы с исходным кодом.
g++ main.cpp math_utils.cpp -o app
В проекте на CMake новый файл нужно добавить в список исходников:
add_executable(app main.cpp math_utils.cpp)
В CLion после этого перезагрузите проект CMake — среда предложит это сама, когда Вы измените CMakeLists.txt.
Причина 3. Объявление и определение не совпадают
// math_utils.h
int square(int x);
// math_utils.cpp
long square(long x) {
return x * x;
}
Для C++ это две разные функции: square(int) и square(long). Вызывается первая, а написана вторая.
Решение: сравните объявление и определение посимвольно — типы параметров, const, пространство имён, имя класса. Имя из сообщения об ошибке показывает, какую именно сигнатуру ищет компоновщик.
Сюда же относится забытое имя класса в определении метода:
// Неправильно: это обычная функция, а не метод класса
double area() { return 3.14159 * radius * radius; }
// Правильно
double Circle::area() { return 3.14159 * radius * radius; }
Причина 4. Не подключена библиотека
Заголовочный файл библиотеки даёт только объявления. Саму библиотеку нужно передать компоновщику флагом -l:
g++ main.cpp -o app -lsqlite3
Порядок аргументов имеет значение: библиотеки указывают после файлов, которые ими пользуются. Команда g++ -lsqlite3 main.cpp -o app может закончиться той же ошибкой.
В CMake библиотеку подключает команда target_link_libraries:
target_link_libraries(app PRIVATE sqlite3)
Причина 5. Шаблон определён в файле .cpp
// max.h
template <typename T>
T max_of(T a, T b);
// max.cpp
template <typename T>
T max_of(T a, T b) {
return a > b ? a : b;
}
При вызове max_of(3, 7) из main.cpp компоновщик сообщит: undefined reference to 'int max_of<int>(int, int)'. Шаблон — это не готовая функция, а образец. Компилятор создаёт по нему функцию там, где шаблон используется, и для этого должен видеть его тело. Тело же лежит в другом файле.
Решение: определяйте шаблоны целиком в заголовочном файле.
Причина 6. Статическое поле класса не определено
struct Counter {
static int count;
};
int main() {
Counter::count = 5;
}
Строка static int count; внутри класса — только объявление. Памяти под переменную ещё не выделено.
Решение: начиная с C++17 достаточно слова inline — поле будет определено прямо в классе.
struct Counter {
inline static int count = 0;
};
В более старом коде определение пишут отдельно, в одном файле .cpp: int Counter::count = 0;.
Причина 7. Нет функции main
undefined reference to `main'
В MinGW на Windows та же проблема выглядит как undefined reference to 'WinMain'. Компоновщик не нашёл точку входа в программу. Возможные причины:
- в проекте нет функции
mainили в её имени опечатка, напримерmian; - файл с
mainне сохранён или не добавлен в сборку; - собирается библиотека, а команда сборки рассчитана на программу.
Причина 8. Виртуальная функция без определения
undefined reference to `vtable for Shape'
Сообщение про vtable пугает, но причина простая: в классе объявлена виртуальная функция, а тела у неё нет.
struct Shape {
virtual double area() const; // объявлена, но не определена
virtual ~Shape() = default;
};
Решение: напишите определение функции. Если в базовом классе у неё не должно быть тела, сделайте её чисто виртуальной: virtual double area() const = 0;.
Две причины, которые встречаются реже
Программу на C++ собирают командой gcc. Тогда не находится стандартная библиотека: undefined reference to 'std::cout'. Для C++ используйте g++ или clang++.
Библиотека написана на C. Компилятор C++ кодирует в имени функции типы её параметров, а компилятор C — нет, поэтому имена не совпадают. Объявления из библиотеки на C заключают в блок extern "C" { ... }. В заголовочных файлах хороших библиотек это уже сделано.
Что проверить по порядку
- Прочитайте имя в сообщении: какая функция или переменная не найдена.
- Найдите её определение в проекте. Если его нет — напишите.
- Убедитесь, что файл с определением попадает в сборку: в команду компилятора или в
CMakeLists.txt. - Сравните сигнатуры объявления и определения, проверьте имя класса и пространство имён.
- Если функция из сторонней библиотеки — проверьте, что библиотека подключена и стоит после исходных файлов.
- Для шаблонов и статических полей — проверьте, что определение доступно там, где оно нужно.
Чем отличается от ошибки «was not declared in this scope»
Ошибку was not declared in this scope выдаёт компилятор: он не знает имени, потому что нет даже объявления — не подключён заголовочный файл или в имени опечатка. undefined reference выдаёт компоновщик: объявление есть, нет определения. По тому, на каком шаге сборка остановилась, понятно, где искать.
Если программа собралась, но падает при запуске, причина уже другая — о самой частой рассказано в статье Segmentation fault в C++. О том, как исходный текст превращается в программу, рассказано в уроке Введение в C++: история, компиляция и первая программа.
Использованная литература
- Definitions and ODR — cppreference.com [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/definition (дата обращения: 08.10.2026).
- Phases of translation — cppreference.com [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/translation_phases (дата обращения: 08.10.2026).
- ISO/IEC 14882:2024 — Язык программирования C++ [Электронный ресурс]. — URL: https://www.iso.org/standard/83626.html (дата обращения: 08.10.2026).