【问题标题】:g++ undefined reference to typeinfog ++未定义对typeinfo的引用
【发布时间】:2010-09-23 08:51:43
【问题描述】:

我刚刚遇到以下错误(并在网上找到了解决方案,但堆栈溢出中不存在):

(.gnu.linkonce.[stuff]): 未定义 参考[方法] [对象 文件]:(.gnu.linkonce.[stuff]): 未定义的对 `typeinfo 的引用 [类名]'

为什么会出现这些“未定义的 typeinfo 引用”链接器错误之一?

(如果你能解释幕后发生的事情,可以加分。)

【问题讨论】:

  • 我知道这是一个旧帖子,但我今天也遇到了同样的问题,解决方法就是在基类中将我的虚函数定义为 virtual abc() {},而不是 virtual abc( );这给出了错误。
  • virtual void abc() =0; 更好(如果从未调用过基本版本)
  • @Nav:如果你像这样定义abc(),你很容易忘记在派生类中重新定义abc(),并认为一切正常,因为你仍然可以毫无问题地调用函数.在this article 中找到了一个实现纯虚函数的好做法,就是让函数打印“Pure virtual function called”然后使程序崩溃。
  • 我遇到了同样的错误。我发现更改对“lib”的引用顺序可能会有所帮助。我刚刚将问题库从列表的开头移到了末尾,这解决了问题
  • GAH。这至少是我第二次准确导航到此页面,阅读@dhardy 的评论并对自己说“Doh”。只花了 45 分钟试图追踪一些疯狂的行为,我只需要= 0;

标签: c++ linker g++


【解决方案1】:

一个可能的原因是你声明了一个虚函数而没有定义它。

当您声明它而不在同一编译单元中定义它时,您表示它是在其他地方定义的 - 这意味着链接器阶段将尝试在其他编译单元(或库)之一中找到它。

定义虚函数的一个例子是:

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

在这种情况下,您将定义附加到声明中,这意味着链接器不需要稍后解析它。

线

virtual void fn();

在没有定义的情况下声明fn(),并且会导致您询问的错误消息。

和代码很相似:

extern int i;
int *pi = &i;

这表明整数 i 在另一个编译单元中声明,必须在链接时解析(否则 pi 不能设置为其地址)。

【讨论】:

  • virtual void fn() = 0 是一个定义是不正确的。它不是一个定义,而仅仅是一个声明。链接器不尝试解析它的唯一原因是相应的 VMT 条目不会引用函数体(最有可能包含空指针)。但是,没有人禁止您以非虚拟方式调用这个纯虚函数,即使用完全限定的名称。在这种情况下,链接器查找主体,您必须定义函数。是的,您可以为纯虚函数定义一个主体。
  • 有时甚至必须为纯虚函数声明一个主体。
  • 编译器 (g++) 会告诉您缺少的符号是什么。注意:在动态库链接的情况下,您可能会得到一个错误的名称。使用 c++filt 以可读的形式获取它。在我的情况下,带有类名的 typeinfo 错误是因为某些基类中缺少虚拟析构函数实现。
  • 问题中特别提到缺少的是typeinfo,这与rtti有关。请参阅 *.com/questions/11904519/… 中 Damon 的评论
  • 我收到此错误是因为 -fno-rtti 被指定为编译器选项,而不是因为未定义虚函数。我认为这个答案的介绍语句是“这个特定的错误是由......引起的”有点误导,而应该是“一个可能的原因是因为......”。
【解决方案2】:

当您混合使用 -fno-rtti-frtti 代码时,也会发生这种情况。然后您需要确保在-frtti 代码中访问type_info 的任何类都使用-frtti 编译其关键方法。当您创建类的对象、使用 dynamic_cast 等时,可能会发生这种访问。

[source]

【讨论】:

【解决方案3】:

当声明的(非纯)虚函数缺少主体时会发生这种情况。在您的类定义中,类似于:

virtual void foo();

应该定义(内联或在链接的源文件中):

virtual void foo() {}

或声明为纯虚拟:

virtual void foo() = 0;

【讨论】:

    【解决方案4】:

    引用the gcc manual:

    对于多态类(具有虚函数的类),type_info 对象与 vtable 一起写出 [...] 对于所有其他类型,我们在使用时写出 type_info 对象:当将 `typeid' 应用于表达式、抛出对象或在 catch 子句或异常规范中引用类型。

    在同一页面上更早一点:

    如果类声明了任何非内联、非纯虚函数,则选择第一个作为类的“关键方法”,并且 vtable 仅在定义关键方法的翻译单元中发出。

    因此,正如其他答案已经提到的那样,当“关键方法”缺少其定义时会发生此错误。

    【讨论】:

    • 就我而言,我有一个基类,它声明但没有定义非纯虚拟的虚拟方法。一旦我把它们变成纯虚拟的,这就是我的意思,链接器错误就消失了。
    • @TatianaRacheva 谢谢!来自链接器的错误报告没有多大帮助,对于大型界面,很容易错过缺少 '=0;'纯虚拟!
    【解决方案5】:

    如果您将一个 .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”。这确实使编译器和链接器变得复杂,可能会损害代码性能。

    【讨论】:

    • 如果基类是抽象的或未使用的,则无需导出(取消隐藏)基类,只需导出非虚函数,通常只需导出构造函数即可。另一方面,如果使用 派生 类,则必须导出它们。
    • 感觉像个黑客,但它确实解决了我这边的症状。谢谢!
    【解决方案6】:

    前面的答案是正确的,但是这个错误也可能是由于试图在一个没有虚函数的类的对象上使用typeid引起的。 C++ RTTI 需要一个 vtable,因此您希望对其执行类型识别的类至少需要一个虚函数。

    如果您希望类型信息在您并不真正需要任何虚函数的类上起作用,请将析构函数设为虚拟。

    【讨论】:

    • Upmoded 因为我认为这更有可能是该特定错误消息的原因(与未定义方法的更一般情况相反......)
    • 我不得不习惯使用 SO 的一件事不是指“高于”答案,因为顺序可能会根据投票而改变。我现在通常不参考任何其他答案,因为它们也可以被删除。我的信念是答案应该是独立的。但是,我仍然引用用户名来进行归因。
    • 你可以在没有 vtable 的情况下使用 typeid;请参阅我对 gcc 手册中引用的回答。
    【解决方案7】:

    我只是在这个错误上花了几个小时,虽然这里的其他答案帮助我了解发生了什么,但它们并没有解决我的特定问题。

    我正在开发一个使用clang++g++ 进行编译的项目。我使用clang++ 没有链接问题,但使用g++ 得到undefined reference to 'typeinfo for 错误。

    重点:将订单事项与g++ 关联。如果您以不正确的顺序列出要链接的库,则会收到 typeinfo 错误。

    有关将订单与gcc/g++ 关联的更多详细信息,请参阅this SO question

    【讨论】:

    • 谢谢!!!我花了一天多的时间试图找出为什么我会收到这个错误并且在我看到这个回复和你链接的那个之前没有任何效果。非常感谢!!
    • 实际上将订单问题与clang 联系起来,所以这个建议是普遍适用的,谢谢。
    • 是的,修复链接顺序最终为我解决了这个问题。对 typeinfo 的未定义引用错误指的是链接类中使用的非虚拟类,错误形式为 somelibrary.a (somefile.o):(.gcc_except_table+0x23c): undefined reference to `typeinfo for NS:CLASSNAME'在这种情况下,NS:CLASSNAME 是在 otherlib.a 库中实现的,需要按照链接顺序将其移动到 somelibrary.a 之下。我还有其他几个与库顺序相关的错误,但这是唯一一个以 typeinfo 错误表现出来的错误。
    【解决方案8】:

    处理 RTTI 和非 RTTI 库的代码的可能解决方案:

    a) 使用 -frtti 或 -fno-rtti 重新编译所有内容
    b) 如果 a) 不适合您,请尝试以下操作:

    假设 libfoo 是在没有 RTTI 的情况下构建的。您的代码使用 libfoo 并使用 RTTI 进行编译。如果您在 libfoo 中使用具有虚拟对象的类 (Foo),您可能会遇到链接时错误,提示:缺少类 Foo 的类型信息。

    定义另一个没有虚拟的类(例如 FooAdapter),并将调用转发到您使用的 Foo。

    在不使用 RTTI 且仅依赖于 libfoo 符号的小型静态库中编译 FooAdapter。为它提供一个标头并在您的代码(使用 RTTI)中使用它。由于 FooAdapter 没有虚函数,它不会有任何类型信息,您将能够链接您的二进制文件。如果你使用了很多来自 libfoo 的不同类,这个解决方案可能不方便,但它是一个开始。

    【讨论】:

    • 这是给我的,链接到具有不同 RTTI 设置的库。
    【解决方案9】:

    在基类(抽象基类)中,您声明了一个虚拟析构函数,并且由于您不能将析构函数声明为纯虚函数,因此您必须在抽象类中定义它,只是一个虚拟定义,如 virtual ~base() { } 可以,或者在任何派生类中。

    如果您未能做到这一点,您将在链接时出现“未定义符号”。 由于 VMT 具有匹配 NULL 的所有纯虚函数的条目,因为它根据派生类中的实现更新表。但对于非纯虚函数,则需要在链接时定义,以便更新VMT表。

    使用 c++filt 对符号进行解缠。像 $c++filt _ZTIN10storageapi8BaseHostE 将输出类似“typeinfo for storageapi::BaseHost”的内容。

    【讨论】:

      【解决方案10】:

      与上面讨论的 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 正常工作而链接对象实现代码的要求。

      【讨论】:

        【解决方案11】:

        我刚才遇到了很多这样的错误。发生的事情是我将一个仅包含头文件的类拆分为一个头文件和一个 cpp 文件。但是,我没有更新我的构建系统,所以 cpp 文件没有被编译。在对标头中声明但未实现的函数的简单引用中,我得到了很多这些类型信息错误。

        解决方案是重新运行构建系统来编译和链接新的 cpp 文件。

        【讨论】:

          【解决方案12】:

          就我而言,我使用了带有头文件和 so 文件的第三方库。我子类化了一个类,当我尝试实例化我的子类时发生了这样的链接错误。

          正如@sergiy 提到的,知道这可能是“rtti”的问题,我设法通过将构造函数实现放入单独的.cpp 文件并将“-fno-rtti”编译标志应用于文件。效果很好。

          由于我仍然不太清楚此链接错误的内部原因,因此我不确定我的解决方案是否通用。但是,我认为在尝试@francois 提到的适配器方式之前值得一试。当然,如果所有源代码都可用(在我的情况下不是),最好尽可能使用“-frtti”重新编译。

          还有一件事,如果您选择尝试我的解决方案,请尝试使单独的文件尽可能简单,并且不要使用 C++ 的一些花哨功能。特别注意 boost 相关的事情,因为它很大程度上取决于 rtti。

          【讨论】:

            【解决方案13】:

            在我的例子中,它是接口类中的一个虚函数,没有被定义为纯虚函数。

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

            我忘记了 = 0 位。

            【讨论】:

              【解决方案14】:

              当我的界面(带有所有纯虚函数)需要另外一个函数并且我忘记将其“设为空”时,我遇到了同样的错误。

              我有

              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 reference to `typeinfo for ICommProvider'

              简单的改变

              virtual int vaClose();
              

              virtual int vaClose() = 0;
              

              解决了这个问题。希望对你有帮助

              【讨论】:

                【解决方案15】:

                我遇到了一种罕见的情况,但这可能会对其他有类似情况的朋友有所帮助。我必须使用 gcc 4.4.7 在旧系统上工作。我必须编译支持c++11或以上的代码,所以我构建了最新版本的gcc 5.3.0。在构建我的代码并链接到依赖项时,如果依赖项是使用较旧的编译器构建的,那么即使我使用 -L/path/to/lib -llibname 明确定义了链接路径,也会出现“未定义的引用”错误。一些包,如 boost 和使用 cmake 构建的项目通常倾向于使用较旧的编译器,它们通常会导致此类问题。您必须走很长的路才能确保他们使用较新的编译器。

                【讨论】:

                  【解决方案16】:

                  在我的情况下,即使我有 dynamic_cast 调用,这纯粹是一个库依赖问题。在 makefile 中添加足够的依赖项后,这个问题就消失了。

                  【讨论】:

                    【解决方案17】:

                    通过此错误消息,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&amp;) 的缺失定义仅报告为第四个错误。因此,有时跳过那些人们不理解的错误消息并首先处理那些易于理解的错误消息是值得的。因为在这种情况下,添加缺少的定义也可以解决未定义类型信息引用的问题。

                    【讨论】:

                      【解决方案18】:

                      检查您的依赖项是否在没有-f-nortti 的情况下编译。

                      对于某些项目,您必须明确设置它,例如在 RocksDB 中:

                      USE_RTTI=1 make shared_lib -j4
                      

                      【讨论】: