Ссылка на неразрешенный внешний символ (возможные причины)
1,00
р.
р.
При попытке сборки программы появляется сообщение об ошибке одного из следующих видов:
ссылка на неразрешенный внешний символ ... неопределённая ссылка на символ ... unresolved external symbol ... undefined reference to ... ошибка LNK2019 ... LNK2001: неразрешенный внешний символ ...
Что это значит, и как исправить такую ошибку?
Ответ Определение Данная ошибка означает, что в процессе компоновки программы, компоновщик не смог найти определение некоторой сущности, на которую есть ссылка (попытка использования) в программе. К таким сущностям может относиться, например, функция или переменная.
Причины и решения Возможных причин появления ошибки может быть несколько и это зависит от того, что представляет из себя собираемый проект. Всё множество ситуаций можно разбить на две большие группы:
1. Используются сторонние или собственные библиотеки Не указана необходимая (статическая) библиотека для компоновщика. Например, к проекту подключен только *.h файл с объявлениями, но отсутствует код реализации, обычно это *.lib или *.a файлы (в зависимости от используемой системы). Требуется явно подключить библиотеку к проекту. Для Visual C++ это можно сделать добавлением следующей строки прямо в код: #pragma comment(lib, "libname.lib") Для gcc/clang требуется указать файл через ключ -l (эль) Для Qt в .pro файле нужно использовать переменную LIBS: LIBS += -L[путь_к_библиотеке] -l[имя_библиотеки] Для системы сборки CMake есть target_link_libraries.
Библиотека указана, но необходимая сущность, например, класс или функция фактически не экспортируется из библиотеки. Под Windows это может возникать из-за отсуствия __declspec(dllexport) перед сущностью. Обычно это решается макросами. Важно, чтобы признак экспортируемости был доступен в той же единице трансляции, где определена экспортируемая сущность. Например, если __declspec(dllexport) указано для функции в заголовочном файле, но этот файл не включили (через #include) в модуль, где определена функция, то эта функция не будет экспортирована. Данная ситуация чаще всего характерна именно для библиотек, находящихся в процессе разработки, и доступных для модификации самому разработчику, нежели для каких-то стабильных версий действительно внешних для проекта библиотек. После разрешения экспортирования библиотеку, конечно же, нужно пересобрать перед непосредственным использованием проблемной сущности. Библиотека указана, но не совпадает разрядность библиотеки и компилируемого кода. В общем случае, разрядность собираемого проекта (приложения или библиотеки) должна совпадать с разрядностью используемой сторонней библиотеки. Обычно производители библиотек предоставляют возможность выбора 32 или 64 бит версию использовать. Если библиотека поставляется в исходных кодах и собирается отдельно от текущего проекта, нужно также выбрать правильную разрядность. Библиотека указана, но она собрана для другой (не совместимой) ОС. Например при сборке проекта в Windows осуществляется попытка использовать бинарный файл, собранный для Linux. В данном случае нужно использовать файлы, подходящие для вашей ОС. Библиотека указана, но она собрана другим компилятором, не совместимым с используемым. Объектные файлы, полученные путем сборки C++ кода разными компиляторами для одной и той же ОС могут быть бинарно несовместимы друг с другом. Требуется использовать совместимые (скорее всего и вовсе одинаковые) компиляторы. Библиотека указана, и собрана тем же компилятором, что и основной проект, но используются разные версии Run-Time библиотек. Например, для Visual C++ возможна ситуация, когда библиотека собрана с ключом /MDd, а основной проект с /MTd. Требуется задать ключи так, чтобы использовались одинаковые версии Run-Time библиотек. Библиотека указана, но нарушен порядок, в котором указаны зависимости. Например, для GCC компоновщика зависимая сущность должна быть указана раньше (левее): foo.o -lz bar.o // просматривает библиотеку 'z' после foo.o, но перед bar.o Если bar.o ссылается на функции в z, то эти функции могут быть не загружены.
2. Сторонние или собственные библиотеки не используются Просто отсутствует определение функции. void f(int) // всего лишь объявление. Нет `тела` функции int main(){ f(42) // undefined reference to `f(int)' } Требуется добавить определение функции f: void f(int) { // тело функции } Иногда определение функции генерируется сторонними утилитами, в частности когда в проекте задействованы какие-то расширения синтаксиса языка. В этом случае надо убедиться, что нужные вспомогательные шаги выполняются. Например, для Qt это Meta-Object Compiler (moc), который генерит определения функций при использовании механизма сигналов/слотов. И чтобы он вызывался для вашего кода, требуется указать вспомогательный макрос Q_OBJECT. Может быть ещё частный случай с ошибкой вида: undefined reference to `vtable for <имя_класса>`
Такая ошибка возникает, если объявленная виртуальная функция класса, не являющаяся чистой (=0), не содержит реализации. class C { virtual void f(int) } Нужно такую реализацию добавить. Если это делается вне класса, надо не забыть указать имя проблемного класса, иначе это будет просто свободная функция, которая не устраняет указанную проблему: void C::f(int) { // виртуальная функция-член вне определения класса // тело функции } void f(int) { // свободная функция, не устраняющая проблему // тело функции } Аналогичная ситуация может возникать при использовании пространств имён, когда объявлении функции находится в пространстве имён: // В заголовочном файле namespace N { void f(int) } а при реализации указать это пространство имён забыли: // В файле реализации void f(int) { // функция в глобальном пространстве имён, не устраняющая проблему // тело функции } namespace N { void f(int) { // функция в нужном пространстве имён // тело функции } } // конец пространства имён Стоит заметить, что C++ разрешает перегрузку функций (существование одноимённых функций, но с разным набором параметров), и в этом случае важно, чтобы сигнатуры функций при объявлении и определении совпадали. Например, вместо объявленной void f(int) была реализована другая: void f(const char*) { // const char* вместо int // тело функции } При вызове f(42) будет ошибка: undefined reference to `f(int)'
Наличие связки шаблонного класса и дружественной функции также может приводить к ошибке. Например: template struct C { friend void f(C) // объявляет *НЕ*шаблонную дружественную функцию } template // определяет шаблонную функцию void f(C) { } int main() { C c f(c) // undefined reference to `f(C)' } Чтобы объявить шаблонную дружественную функцию, требуется добавить указание шаблонности: template struct C { template // добавили шаблонность для friend функции friend void f(C) } Важно, что имя шаблонного параметра для дружественной функции должно отличаться от имени параметра шаблонного класса T, т.к. иначе будет ошибка о сокрытии имени. В частном случае имя класса можно вовсе не указывать, и оставить template . Но это не всегда будет правильным решением, т.к. фактически могла потребоваться дружественная специализация шаблона функции, а не сама шаблонная функция. Подробнее об использовании дружественных функций в шаблонном классе можно ознакомиться здесь. Отсутствует определение статической переменной класса. struct S { static int i } int main() { S s s.i = 42 // undefined reference to `S::i' } Нужно добавить определение (выделить память) переменной: int S::i Неправильная реализация шаблонного кода. Например, реализация шаблонного кода помещена в *.cpp файл, хотя она должна находиться полностью в подключаемом *.h файле. Это требуется по той причине, что компилятор обрабатывает каждый модуль независимо, и в момент инстанцирования шаблона (подстановки конкретного типа) код его реализации должен быть виден. При этом если возможные типы шаблона известны заранее, можно произвести инстанцирование сразу рядом с телом шаблона и не предоставлять его наружу в исходном коде заголовочного файла. Пример: // unit.h #pragma once template T f(T) // объявление шаблона без реализации // unit.cpp #include "unit.h" template T f(T t) { return t + t } // реализация шаблона template int f(int) // явное инстанцирование для int template double f(double) // явное инстанцирование для double // main.cpp #include "unit.h" int main() { f(2) // ok int f(1.5) // ok double f('a') // undefined reference to `char f(char)' } Файл с кодом не был скомпилирован. Например, в случае использования make-файла не было прописано правило построения файла, а в случае использования IDE типа Visual Studio *.cpp файл не добавлен в список файлов проекта. Виртуальная функция в базовом классе не объявлена как = 0 (pure-virtual). struct B { virtual void f() } struct D : B { void f() {} } int main() { D d } При использовании иерархии классов функция в базовом классе, не имеющая реализации должна быть помечена как "чистая": struct B { virtual void f() = 0 } Имя не имеет внешнего связывания. Например, есть объявление функции f в модуле А и даже ее реализация в модуле B, но реализация помечена как static: // A.cpp void f() int main() { f() // undefined reference to `f()' } // B.cpp static void f() {} Аналогичная ситуация может возникнуть при использовании безымянного пространства имен: // B.cpp namespace { void f() {} } Или даже при наличии inline у функции: // B.cpp inline void f() {}