g++ неопределенная ссылка на typeinfo

Я просто наткнулся на следующую ошибку (и нашел решение в сети, но его нет в переполнении стека):

(.gnu.linkonce. [stuff]): неопределенная ссылка на [метод] [объектный файл]:(.gnu.linkonce.[stuff]): неопределенная ссылка на `typeinfo для [classname]'

Почему можно получить одну из этих ошибок компоновщика "неопределенная ссылка на typeinfo"?

(Бонусные баллы, если вы можете объяснить, что происходит за кулисами.)

18 ответов

Решение

Одна из возможных причин заключается в том, что вы объявляете виртуальную функцию, не определяя ее.

Когда вы объявляете это, не определяя его в том же модуле компиляции, вы указываете, что он определен где-то еще - это означает, что фаза компоновщика будет пытаться найти его в одном из других модулей компиляции (или библиотек).

Примером определения виртуальной функции является:

virtual void fn() { /* insert code here */ }

В этом случае вы присоединяете определение к объявлению, что означает, что компоновщику не нужно разрешать его позже.

Линия

virtual void fn();

объявляет fn() без определения и приведет к сообщению об ошибке, о котором вы спрашивали.

Это очень похоже на код:

extern int i;
int *pi = &i;

в котором говорится, что целое число i объявляется в другом модуле компиляции, который должен быть разрешен во время ссылки (в противном случае pi не может быть установлен на его адрес).

Это также может произойти, когда вы смешиваете -fno-rtti а также -frtti код. Тогда вам нужно убедиться, что любой класс, который type_info доступ в -frtti код, скомпилируйте их ключевой метод с -frtti, Такой доступ может произойти, когда вы создаете объект класса, используя dynamic_cast и т.п.

[ источник]

Это происходит, когда в объявленных (не чистых) виртуальных функциях отсутствуют тела. В вашем определении класса, что-то вроде:

virtual void foo();

Должен быть определен (встроенный или в связанном исходном файле):

virtual void foo() {}

Или объявлен чисто виртуальным:

virtual void foo() = 0;

Цитирование из руководства gcc:

Для полиморфных классов (классов с виртуальными функциями) объект type_info записывается вместе с vtable [...]. Для всех других типов мы выписываем объект type_info, когда он используется: при применении `typeid 'к выражению, бросая объект или ссылаясь на тип в предложении catch или в спецификации исключений.

И чуть раньше на той же странице:

Если класс объявляет какие-либо не встроенные, не чистые виртуальные функции, первая из них выбирается в качестве "ключевого метода" для класса, а vtable генерируется только в модуле перевода, где определен ключевой метод.

Таким образом, эта ошибка возникает, когда "ключевой метод" не имеет определения, как уже упоминалось в других ответах.

Если вы связываете один.so с другим, еще одной возможностью является компиляция с "-fvisibility=hidden" в gcc или g++. Если оба файла.so были созданы с "-fvisibility=hidden" и метод ключа не совпадает с.so, как другая реализация виртуальной функции, последний не увидит vtable или typeinfo первого. Для линкера это выглядит как нереализованная виртуальная функция (как в ответах paxdiablo и cdleary).

В этом случае вы должны сделать исключение для видимости базового класса с

__attribute__ ((visibility("default")))

в объявлении класса. Например,

class __attribute__ ((visibility("default"))) boom{
    virtual void stick();
}

Другое решение, конечно, состоит в том, чтобы не использовать "-fvisibility=hidden". Это усложняет работу компилятора и компоновщика, возможно, в ущерб производительности кода.

Предыдущие ответы верны, но эта ошибка также может быть вызвана попыткой использовать typeid для объекта класса, который не имеет виртуальных функций. C++ RTTI требует vtable, поэтому классы, для которых вы хотите выполнить идентификацию типа, требуют как минимум одну виртуальную функцию.

Если вы хотите, чтобы информация о типах работала с классом, для которого вы на самом деле не хотите никаких виртуальных функций, сделайте деструктор виртуальным.

Я просто потратил несколько часов на эту ошибку, и хотя другие ответы здесь помогли мне понять, что происходит, они не решили мою конкретную проблему.

Я работаю над проектом, который компилируется с использованием обоих clang++ а также g++, У меня не было проблем со связыванием clang++, но получал undefined reference to 'typeinfo for ошибка с g++,

Важный момент: связь порядка вопросов с g++, Если вы перечислите библиотеки, которые хотите связать, в неправильном порядке, вы можете получить typeinfo ошибка.

Посмотрите этот ТАК вопрос для более подробной информации о связывании заказа с gcc/g++,

Возможные решения для кода, которые работают с библиотеками RTTI и не-RTTI:

а) Перекомпилировать все с помощью -frtti или -fno-rtti
б) Если а) не возможно для вас, попробуйте следующее:

Предположим, что libfoo собран без RTTI. Ваш код использует libfoo и компилируется с RTTI. Если вы используете класс (Foo) в libfoo, который имеет виртуалы, вы, скорее всего, столкнетесь с ошибкой во время компоновки, которая говорит: отсутствует typeinfo для класса Foo.

Определите другой класс (например, FooAdapter), который не имеет виртуальных и будет перенаправлять вызовы в Foo, который вы используете.

Скомпилируйте FooAdapter в небольшой статической библиотеке, которая не использует RTTI и зависит только от символов libfoo. Обеспечьте заголовок для этого и используйте это вместо этого в своем коде (который использует RTTI). Поскольку FooAdapter не имеет виртуальной функции, у него не будет никакой информации о типе, и вы сможете связать свой двоичный файл. Если вы используете много разных классов из libfoo, это решение может быть не удобным, но это только начало.

В моем случае это была виртуальная функция в классе интерфейса, который не был определен как чистый виртуальный.

class IInterface
{
public:
  virtual void Foo() = 0;
}

Я забыл = 0 немного.

В базовом классе (абстрактный базовый класс) вы объявляете виртуальный деструктор и, поскольку вы не можете объявить деструктор как чисто виртуальную функцию, вы должны определить его прямо здесь, в абстрактном классе, просто фиктивное определение, подобное виртуальному ~base() { } или в любом производном классе.

Если вы не сделаете этого, вы получите "неопределенный символ" во время ссылки. Поскольку VMT имеет запись для всех чисто виртуальных функций с совпадающим NULL, он обновляет таблицу в зависимости от реализации в производном классе. Но для не чистых, но виртуальных функций, ему нужно определение во время соединения, чтобы он мог обновить таблицу VMT.

Используйте C++ Filter, чтобы разобрать символ. Например, $ C++ Filter _ZTIN10storageapi8BaseHostE выведет что-то вроде "typeinfo для storageapi::BaseHost".

Подобно обсуждению 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 работать должным образом.

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

как упомянул @sergiy, зная, что это может быть проблемой 'rtti', мне удалось обойти ее, поместив реализацию конструктора в отдельный файл.cpp и применив к нему флаги компиляции '-fno-rtti'. это работает хорошо.

так как я все еще не совсем понимаю внутреннюю ошибку этой ссылки, я не уверен, является ли мое решение общим. однако, я думаю, что стоит попробовать, прежде чем пытаться использовать адаптер, как упомянуто @francois . и, конечно, если все исходные коды доступны (не в моем случае), лучше перекомпилируйте с '-frtti', если это возможно.

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

Я получил много этих ошибок только сейчас. Случилось так, что я разделил класс только для заголовочного файла на заголовочный файл и файл cpp. Однако я не обновил свою систему сборки, поэтому файл cpp не был скомпилирован. Среди простых неопределенных ссылок на функции, объявленные в заголовке, но не реализованные, я получил много этих ошибок типа info.

Решением было перезапустить систему сборки, чтобы скомпилировать и связать новый файл cpp.

У меня та же ошибка, когда моему интерфейсу (со всеми чисто виртуальными функциями) требовалась еще одна функция, и я забыл обнулить ее.

я имел

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): неопределенная ссылка на `typeinfo для ICommProvider'

Простое изменение от

virtual int vaClose();

в

virtual int vaClose() = 0;

исправил проблему. Надеюсь, поможет

Я сталкиваюсь с редкой ситуацией, но это может помочь другим друзьям в подобной ситуации. Я должен работать на более старой системе с gcc 4.4.7. Я должен скомпилировать код с поддержкой C++11 или выше, поэтому я собираю последнюю версию gcc 5.3.0. При сборке моего кода и создании ссылок на зависимости, если зависимость создается с помощью более старого компилятора, я получил ошибку "неопределенная ссылка на", хотя я четко определил путь ссылки с помощью -L/path/to/lib -llibname. Некоторые пакеты, такие как boost и проекты, собранные с помощью cmake, обычно имеют тенденцию использовать более старый компилятор, и они обычно вызывают такие проблемы. Вы должны пройти долгий путь, чтобы убедиться, что они используют более новый компилятор.

Этим сообщением об ошибке компоновщик G++ сообщает вам, что он не может собрать полный дескриптор статической информации о типе для данного класса, когда это необходимо. Как многие уже отмечали, это, скорее всего, связано с отсутствием определений виртуальных функций.

Плохо, однако, то, что порядок сообщений об ошибках может быть нелогичным, поскольку «неопределенная ссылка на typeinfo» встречается перед неопределенными ссылками на отсутствующие виртуальные определения. Вот пример, который я только что испытал:

      /usr/bin/ld: module.o:(.data.rel.ro+0x10): undefined reference to `typeinfo for type_xxx'
/usr/bin/ld: module.o:(.data.rel.ro+0x28): undefined reference to `typeinfo for type_xxx'
/usr/bin/ld: module.o:(.data.rel.ro+0x40): undefined reference to `typeinfo for type_xxx'
/usr/bin/ld: module.o:(.data.rel.ro+0x150): undefined reference to `type_xxx::has_property(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'

Итак, это недостающее определение type_xxx::has_property(const std::string&)сообщается только как четвертая ошибка. Так что иногда имеет смысл пропустить непонятные сообщения об ошибках и обработать, во-первых, те, которые легко понять. Потому что в этом случае добавление недостающих определений также решает проблему с неопределенными ссылками на typeinfo.

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

Убедитесь, что ваши зависимости были скомпилированы без -f-nortti,

Для некоторых проектов вы должны установить это явно, как в RocksDB:

USE_RTTI=1 make shared_lib -j4
Другие вопросы по тегам