我刚刚遇到以下错误(并在网上找到了解决方案,但堆栈溢出中不存在):
(.gnu.linkonce.[stuff]): 未定义引用 [method] [object file]:(.gnu.linkonce.[stuff]): 未定义引用 `typeinfo for [classname]'
为什么会出现这些“未定义的 typeinfo 引用”链接器错误之一?
(如果您能解释幕后发生的事情,则可以加分。)
一个可能的原因是因为您声明了一个虚函数而没有定义它。
当您声明它而不在同一编译单元中定义它时,您表示它是在其他地方定义的 - 这意味着链接器阶段将尝试在其他编译单元(或库)之一中找到它。
定义虚函数的一个例子是:
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 链接到另一个,还有一种可能性是在 gcc 或 g++ 中使用“-fvisibility=hidden”进行编译。如果两个 .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
错误。
有关使用/链接订单的更多详细信息,请参阅此 SO 问题。gcc
g++
处理 RTTI 和非 RTTI 库的代码的可能解决方案:
a) 使用 -frtti 或 -fno-rtti 重新编译所有内容
b) 如果 a) 不适合您,请尝试以下操作:
假设 libfoo 是在没有 RTTI 的情况下构建的。您的代码使用 libfoo 并使用 RTTI 进行编译。如果您在 libfoo 中使用具有虚拟对象的类 (Foo),您可能会遇到链接时错误,提示:缺少类 Foo 的类型信息。
定义另一个没有虚拟的类(例如 FooAdapter),并将调用转发给您使用的 Foo。
在不使用 RTTI 且仅依赖于 libfoo 符号的小型静态库中编译 FooAdapter。为它提供一个标头并在您的代码(使用 RTTI)中使用它。由于 FooAdapter 没有虚函数,它不会有任何类型信息,您将能够链接您的二进制文件。如果你使用了很多来自 libfoo 的不同类,这个解决方案可能不方便,但它是一个开始。
在基类(抽象基类)中,您声明了一个虚拟析构函数,并且由于您不能将析构函数声明为纯虚函数,因此您必须在抽象类中定义它,只是一个虚拟定义,如 virtual ~base( ) { } 会做,或者在任何派生类中。
如果你不这样做,你将在链接时得到一个“未定义的符号”。由于 VMT 具有匹配 NULL 的所有纯虚函数的条目,因为它根据派生类中的实现更新表。但对于非纯虚函数,需要在链接时定义,以便更新VMT表。
使用 c++filt 来对符号进行分解。像 $c++filt _ZTIN10storageapi8BaseHostE 将输出类似“typeinfo for 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 正常工作而链接对象实现代码的要求。
我刚才遇到了很多这样的错误。发生的事情是我将一个仅包含头文件的类拆分为一个头文件和一个 cpp 文件。但是,我没有更新我的构建系统,所以 cpp 文件没有被编译。在简单地对标头中声明但未实现的函数的未定义引用中,我得到了很多这些类型信息错误。
解决方案是重新运行构建系统来编译和链接新的 cpp 文件。
就我而言,我使用了带有头文件和so文件的第三方库。我子类化了一个类,当我尝试实例化我的子类时发生了这样的链接错误。
正如@sergiy 所提到的,知道这可能是“rtti”的问题,我设法通过将构造函数实现放入单独的.cpp 文件并将“-fno-rtti”编译标志应用于文件来解决它。它运作良好。
由于我仍然不太清楚此链接错误的内部原因,因此我不确定我的解决方案是否通用。但是,我认为在尝试@francois 提到的适配器方式之前值得一试。当然,如果所有源代码都可用(在我的情况下不是),最好尽可能使用“-frtti”重新编译。
还有一件事,如果您选择尝试我的解决方案,请尝试使单独的文件尽可能简单,并且不要使用 C++ 的一些花哨功能。特别注意提升相关的事情,因为它很大程度上取决于rtti。
在我的例子中,它是一个接口类中的一个虚函数,它没有被定义为一个纯虚函数。
class IInterface
{
public:
virtual void Foo() = 0;
}
我忘了= 0
一点。
当我的界面(具有所有纯虚函数)需要另外一个功能并且我忘记将其“设为空”时,我遇到了同样的错误。
我有
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): 未定义对“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 构建的项目通常倾向于使用较旧的编译器,它们通常会导致此类问题。您必须走很长的路才能确保他们使用较新的编译器。
在我的情况下,即使我有 dynamic_cast 调用,这纯粹是一个库依赖问题。在 makefile 中添加足够的依赖项后,这个问题就消失了。
有了这个错误消息,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&)
仅报告为第四个错误。因此,有时跳过那些人们不理解的错误消息并首先处理那些易于理解的错误消息是值得的。因为在这种情况下,添加缺少的定义也可以解决未定义类型信息引用的问题。
检查您的依赖项是否在没有-f-nortti
.
对于某些项目,您必须明确设置它,例如在 RocksDB 中:
USE_RTTI=1 make shared_lib -j4