G++ undefined ссылка на typeinfo
Я просто наткнулся на следующую ошибку (и нашел решение онлайн, но не присутствовал в переполнении стека):
(. gnu.linkonce. [материал]): undefinedссылка на [метод] [объект файл]:( gnu.linkonce [материал])..: undefined ссылка на `typeinfo для [Имя_класс] '
Почему можно получить одну из этих ссылок "undefined ссылка на typeinfo" ошибки компоновщика?
(Бонусные баллы, если вы можете объяснить, что происходит за кулисами.)
Ответы
Ответ 1
Одна из возможных причин заключается в том, что вы объявляете виртуальную функцию без ее определения.
Когда вы объявляете его без определения его в том же компиляционном блоке, вы указываете, что он определен где-то в другом месте - это означает, что этап компоновщика попытается найти его в одном из других блоков компиляции (или библиотек).
Пример определения виртуальной функции:
virtual void fn() { /* insert code here */ }
В этом случае вы присоединяете определение к объявлению, что означает, что компоновщик не нуждается в дальнейшем разрешении.
Линия
virtual void fn();
объявляет fn()
без определения и вызывает сообщение об ошибке, о котором вы просили.
Он очень похож на код:
extern int i;
int *pi = &i;
в котором указано, что целое число i
объявлено в другом модуле компиляции, которое должно быть разрешено во время связи (иначе pi
не может быть установлен на его адрес).
Ответ 2
Это также может произойти, когда вы смешиваете код -fno-rtti
и -frtti
. Затем вам нужно убедиться, что любой класс, к которому type_info
обращается в коде -frtti
, имеет свой ключевой метод, скомпилированный с помощью -frtti
. Такой доступ может произойти, когда вы создаете объект класса, используйте dynamic_cast
и т.д.
[источник]
Ответ 3
Это происходит, когда объявленным (нечистым) виртуальным функциям не хватает тел. В определении вашего класса что-то вроде:
virtual void foo();
Должен быть определен (встроенный или связанный исходный файл):
virtual void foo() {}
Или объявлен чистый виртуальный:
virtual void foo() = 0;
Ответ 4
Цитата из руководство gcc:
Для полиморфных классов (классы с виртуальными функциями) объект type_info выписывается вместе с vtable [...] Для всех других типов мы выписываем объект type_info, когда он используется: при применении типа typeid к выражение, бросание объекта или обращение к типу в предложении catch или спецификации исключения.
И немного раньше на той же странице:
Если класс объявляет любые нестрочные, нечистые виртуальные функции, первый выбирается как "ключевой метод" для класса, а vtable испускается только в блоке перевода, где определяется ключевой метод.
Итак, эта ошибка возникает, когда "ключевой метод" не имеет своего определения, как уже упоминалось в других ответах.
Ответ 5
Если вы связываете один .so с другим, еще одна возможность заключается в компиляции с "-fvisibility = hidden" в gcc или g++. Если оба файла .so были построены с помощью "-fvisibility = hidden" , а ключевой метод не в том же. Как и в другой реализации виртуальных функций, последний не увидит vtable или typeinfo из прежнего. Для компоновщика это выглядит как нереализованная виртуальная функция (как в paxdiablo и cdleary ответах).
В этом случае вы должны сделать исключение для видимости базового класса с
__attribute__ ((visibility("default")))
в объявлении класса. Например,
class __attribute__ ((visibility("default"))) boom{
virtual void stick();
}
Другим решением, конечно же, является не использование "-fvisibility = hidden" . Это усложняет работу компилятора и компоновщика, возможно, в ущерб производительности кода.
Ответ 6
Предыдущие ответы верны, но эта ошибка также может быть вызвана попыткой использовать typeid для объекта класса, который не имеет виртуальных функций. Для С++ RTTI требуется vtable, поэтому для классов, для которых требуется выполнить идентификацию типа, требуется хотя бы одна виртуальная функция.
Если вы хотите, чтобы информация о типе работала над классом, для которого вам действительно не нужны виртуальные функции, сделайте виртуальный деструктор.
Ответ 7
Возможные решения для кода, использующего библиотеки RTTI и не RTTI:
a) Перекомпилируйте все с помощью -frtti или -fno-rtti
b) Если для вас не возможно а), попробуйте следующее:
Предположим, что libfoo построен без RTTI. Ваш код использует libfoo и компилируется с RTTI. Если вы используете класс (Foo) в libfoo, у которого есть виртуальные машины, вы, вероятно, столкнетесь с ошибкой времени ссылки, которая говорит: missing typeinfo для класса Foo.
Определите другой класс (например, FooAdapter), который не имеет виртуальных и переадресовывает вызовы Foo, которые вы используете.
Скомпилируйте FooAdapter в небольшой статической библиотеке, которая не использует RTTI и зависит только от символов libfoo. Предоставьте заголовок для него и используйте это вместо этого в своем коде (который использует RTTI). Поскольку FooAdapter не имеет виртуальной функции, у него не будет никакого типаinfo, и вы сможете связать свой двоичный файл. Если вы используете много разных классов из libfoo, это решение может быть неудобно, но это начало.
Ответ 8
Я только что потратил несколько часов на эту ошибку, и, хотя другие ответы здесь помогли мне понять, что происходит, они не исправили мою проблему.
Я работаю над проектом, который компилируется с использованием как clang++
, так и g++
. У меня не возникало проблем с связыванием, используя clang++
, но получал ошибку undefined reference to 'typeinfo for
с g++
.
Важный момент: Связывание со ссылками MATTERS с g++
. Если вы перечислите библиотеки, которые хотите связать, в неправильном порядке, вы можете получить ошибку typeinfo
.
См. этот вопрос SO для получения более подробной информации о порядке привязки с помощью gcc
/g++
.
Ответ 9
Аналогично обсуждению RTTI, NO-RTTI выше, эта проблема также может возникать, если вы используете dynamic_cast и не включаете объектный код, содержащий реализацию класса.
Я столкнулся с этой проблемой, создав Cygwin, а затем портировал код в Linux. Файлы make, структура каталогов и даже версии gcc (4.8.2) были идентичны в обоих случаях, но код был связан и работал правильно на Cygwin, но не смог подключиться к Linux. Red Hat Cygwin, по-видимому, сделал модификаторы компилятора/компоновщика, которые избегают требования связывания объектного кода.
Сообщение об ошибке компоновщика Linux правильно направило меня на строку dynamic_cast, но более ранние сообщения на этом форуме заставили меня искать недостающие функции, а не фактическую проблему: отсутствующий код объекта. Моим обходным решением было подставить функцию виртуального типа в базовый и производный класс, например. virtual int isSpecialType(), а не использовать dynamic_cast. Этот метод позволяет избежать необходимости связывать код реализации объекта только для правильной работы dynamic_cast.
Ответ 10
В базовом классе (абстрактный базовый класс) вы объявляете виртуального деструктора и, поскольку вы не можете объявить деструктор как чистую виртуальную функцию, либо вы должны определить его здесь, в абстрактном классе, просто фиктивное определение, такое как виртуальный ~ base() {} будет делать или в любом из производного класса.
Если вы этого не сделаете, в конце страницы вы получите символ "undefined".
Поскольку VMT имеет запись для всех чистых виртуальных функций с соответствующим NULL, поскольку она обновляет таблицу в зависимости от реализации в производном классе. Но для нечистых, но виртуальных функций ему требуется определение во время связи, чтобы он мог обновлять таблицу VMT.
Используйте С++ filter для демонстрации символа. Как $С++ filter _ZTIN10storageapi8BaseHostE
выведет что-то вроде "typeinfo для storageapi:: BaseHost".
Ответ 11
Я получил много этих ошибок только сейчас. Случилось так, что я разделил класс только для заголовка в файл заголовка и файл cpp. Однако я не обновлял свою систему сборки, поэтому файл cpp не скомпилировался. Среди просто ссылок undefined на функции, объявленные в заголовке, но не реализованные, я получил много этих ошибок типаinfo.
Решение заключалось в повторном запуске системы сборки для компиляции и связывания нового файла cpp.
Ответ 12
в моем случае, я использовал стороннюю библиотеку с файлами заголовков и таким образом файл. я подклассифицировал один класс, и ошибка связи, как это произошло, когда я пытаюсь создать экземпляр моего подкласса.
как упоминалось в @sergiy, известно, что это может быть проблемой "rtti", мне удалось обойти это с помощью поместить реализацию конструктора в отдельный .cpp файл и применить флаги компиляции "-fno-rtti" к файл. он работает хорошо.
так как я до сих пор не совсем понимаю о внутренней ошибке этой ссылки, я не уверен, является ли мое решение общим. однако, я думаю, что это стоит сделать, прежде чем пытаться использовать адаптер, как упоминалось @francois. и, конечно, если все исходные коды доступны (не в моем случае), лучше перекомпилируйте с помощью "-frtti", если это возможно.
еще одна вещь, если вы решите попробовать мое решение, попробуйте сделать отдельный файл максимально простым и не использовать некоторые причудливые функции С++. обратите особое внимание на вещи, связанные с повышением, потому что многое зависит от rtti.
Ответ 13
У меня такая же ошибка, когда мой интерфейс (со всеми чистыми виртуальными функциями) нуждался в еще одной функции, и я забыл его "null".
У меня был
class ICommProvider
{
public:
/**
* @brief If connection is established, it sends the message into the server.
* @param[in] msg - message to be send
* @return 0 if success, error otherwise
*/
virtual int vaSend(const std::string &msg) = 0;
/**
* @brief If connection is established, it is waiting will server response back.
* @param[out] msg is the message received from server
* @return 0 if success, error otherwise
*/
virtual int vaReceive(std::string &msg) = 0;
virtual int vaSendRaw(const char *buff, int bufflen) = 0;
virtual int vaReceiveRaw(char *buff, int bufflen) = 0;
/**
* @bief Closes current connection (if needed) after serving
* @return 0 if success, error otherwise
*/
virtual int vaClose();
};
Последний vaClose не является виртуальным, поэтому компиляция не знала, где получить реализацию для него и тем самым запуталась. мое сообщение было:
... TCPClient.o:(. rodata + 0x38): undefined ссылка на `typeinfo для ICommProvider '
Простое изменение из
virtual int vaClose();
к
virtual int vaClose() = 0;
исправлена проблема. надеюсь, что это поможет
Ответ 14
Я встречаюсь с ситуацией, которая встречается редко, но это может помочь другим друзьям в подобной ситуации. Мне нужно работать с более старой системой с gcc 4.4.7. Мне нужно скомпилировать код с поддержкой С++ 11 или выше, поэтому я создаю последнюю версию gcc 5.3.0. При создании моего кода и привязке к зависимостям, если зависимость создается с помощью более старого компилятора, я получил ссылку "undefined на ошибку", хотя я четко определил путь связывания с -L/path/to/lib -llibname. Некоторые пакеты, такие как boost и project build with cmake, обычно имеют тенденцию использовать старый компилятор, и они обычно вызывают такие проблемы. Вы должны пройти долгий путь, чтобы убедиться, что они используют новый компилятор.
Ответ 15
В моем случае это просто проблема зависимости от библиотеки, даже если у меня есть вызов dynamic_cast. После добавления достаточного количества зависимостей в make файл эта проблема исчезла.
Ответ 16
Убедитесь, что ваши зависимости были скомпилированы без -f-nortti
.
Для некоторых проектов вы должны установить это явно, как в RocksDB:
USE_RTTI=1 make shared_lib -j4