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