Ошибка undefined reference в C++: причины и как исправить

Что означает ошибка undefined reference to в C++ и как её исправить: восемь частых причин с примерами — от забытого файла в сборке до шаблонов, статических полей и vtable.

Содержание

undefined reference to ... — ошибка компоновщика. Она означает: программа использует функцию или переменную, которую Вы объявили, но её определение не нашлось ни в одном из файлов, из которых собирается программа. Синтаксис при этом в порядке, поэтому искать опечатку в строке, на которую указывает сообщение, обычно бесполезно.

Откуда берётся ошибка

Сборка программы на C++ проходит в два шага.

  1. Компиляция. Каждый файл .cpp отдельно превращается в объектный файл. Компилятору достаточно объявления функции — строки вида int square(int x);. Он верит, что определение существует где-то ещё.
  2. Компоновка. Компоновщик (linker) соединяет объектные файлы и библиотеки в одну программу и подставляет адреса функций на места их вызовов. Если определения нигде нет, подставлять нечего — возникает undefined reference.

Поэтому ошибка появляется в самом конце сборки, уже после того, как все файлы скомпилировались без замечаний.

Как прочитать сообщение

В Linux и в MinGW на Windows сообщение выглядит так:

TXT
/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, MinGWundefined reference to 'square(int)'
macOSUndefined symbols for architecture arm64: "square(int)", referenced from: _main
Visual Studioerror LNK2019: unresolved external symbol "int __cdecl square(int)"

Причины и способы исправления во всех трёх случаях одинаковые.

Причина 1. Функция объявлена, но не написана

C++
#include <iostream>

int square(int x);

int main() {
    std::cout << square(5) << '\n';
}

Объявление есть, тела функции нет нигде. Компилятор доволен, компоновщик — нет.

Решение: напишите определение функции.

C++
int square(int x) {
    return x * x;
}

Причина 2. Файл с определением не участвует в сборке

Это самая частая причина. Функция написана в math_utils.cpp, но собирается только main.cpp:

Bash
g++ main.cpp -o app

Компоновщик не знает о втором файле и не находит square.

Решение: передайте компилятору все файлы с исходным кодом.

Bash
g++ main.cpp math_utils.cpp -o app

В проекте на CMake новый файл нужно добавить в список исходников:

TXT
add_executable(app main.cpp math_utils.cpp)

В CLion после этого перезагрузите проект CMake — среда предложит это сама, когда Вы измените CMakeLists.txt.

Причина 3. Объявление и определение не совпадают

C++
// math_utils.h
int square(int x);

// math_utils.cpp
long square(long x) {
    return x * x;
}

Для C++ это две разные функции: square(int) и square(long). Вызывается первая, а написана вторая.

Решение: сравните объявление и определение посимвольно — типы параметров, const, пространство имён, имя класса. Имя из сообщения об ошибке показывает, какую именно сигнатуру ищет компоновщик.

Сюда же относится забытое имя класса в определении метода:

C++
// Неправильно: это обычная функция, а не метод класса
double area() { return 3.14159 * radius * radius; }

// Правильно
double Circle::area() { return 3.14159 * radius * radius; }

Причина 4. Не подключена библиотека

Заголовочный файл библиотеки даёт только объявления. Саму библиотеку нужно передать компоновщику флагом -l:

Bash
g++ main.cpp -o app -lsqlite3

Порядок аргументов имеет значение: библиотеки указывают после файлов, которые ими пользуются. Команда g++ -lsqlite3 main.cpp -o app может закончиться той же ошибкой.

В CMake библиотеку подключает команда target_link_libraries:

TXT
target_link_libraries(app PRIVATE sqlite3)

Причина 5. Шаблон определён в файле .cpp

C++
// 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. Статическое поле класса не определено

C++
struct Counter {
    static int count;
};

int main() {
    Counter::count = 5;
}

Строка static int count; внутри класса — только объявление. Памяти под переменную ещё не выделено.

Решение: начиная с C++17 достаточно слова inline — поле будет определено прямо в классе.

C++
struct Counter {
    inline static int count = 0;
};

В более старом коде определение пишут отдельно, в одном файле .cpp: int Counter::count = 0;.

Причина 7. Нет функции main

TXT
undefined reference to `main'

В MinGW на Windows та же проблема выглядит как undefined reference to 'WinMain'. Компоновщик не нашёл точку входа в программу. Возможные причины:

  • в проекте нет функции main или в её имени опечатка, например mian;
  • файл с main не сохранён или не добавлен в сборку;
  • собирается библиотека, а команда сборки рассчитана на программу.

Причина 8. Виртуальная функция без определения

TXT
undefined reference to `vtable for Shape'

Сообщение про vtable пугает, но причина простая: в классе объявлена виртуальная функция, а тела у неё нет.

C++
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" { ... }. В заголовочных файлах хороших библиотек это уже сделано.

Что проверить по порядку

  1. Прочитайте имя в сообщении: какая функция или переменная не найдена.
  2. Найдите её определение в проекте. Если его нет — напишите.
  3. Убедитесь, что файл с определением попадает в сборку: в команду компилятора или в CMakeLists.txt.
  4. Сравните сигнатуры объявления и определения, проверьте имя класса и пространство имён.
  5. Если функция из сторонней библиотеки — проверьте, что библиотека подключена и стоит после исходных файлов.
  6. Для шаблонов и статических полей — проверьте, что определение доступно там, где оно нужно.

Чем отличается от ошибки «was not declared in this scope»

Ошибку was not declared in this scope выдаёт компилятор: он не знает имени, потому что нет даже объявления — не подключён заголовочный файл или в имени опечатка. undefined reference выдаёт компоновщик: объявление есть, нет определения. По тому, на каком шаге сборка остановилась, понятно, где искать.

Если программа собралась, но падает при запуске, причина уже другая — о самой частой рассказано в статье Segmentation fault в C++. О том, как исходный текст превращается в программу, рассказано в уроке Введение в C++: история, компиляция и первая программа.

Использованная литература

  1. Definitions and ODR — cppreference.com [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/definition (дата обращения: 08.10.2026).
  2. Phases of translation — cppreference.com [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/translation_phases (дата обращения: 08.10.2026).
  3. ISO/IEC 14882:2024 — Язык программирования C++ [Электронный ресурс]. — URL: https://www.iso.org/standard/83626.html (дата обращения: 08.10.2026).