typeinfoへのg ++​​未定義参照


209

私は次のエラーに遭遇しました(そして解決策はオンラインで見つかりましたが、Stack Overflowにはありません)。

(.gnu.linkonce。[stuff]):[メソッド] [オブジェクトファイル]への未定義の参照:(。gnu.linkonce。[stuff]): `[クラス名]のtypeinfoへの未定義の参照 '

これらの「typeinfoへの未定義の参照」リンカーエラーの1つが表示されるのはなぜですか?

(舞台裏で何が起こっているかを説明できる場合のボーナスポイント。)


31
古い投稿であることはわかっていますが、今日も同じ問題がありました。解決策は、仮想関数を仮想abc()ではなく、基本クラスの仮想abc(){}として定義することでした。エラーが発生しました。
ナビゲーション

15
より良いvirtual void abc() =0;(基本バージョンが呼び出されない場合)
dhardy 2012

3
@Nav:このabc()ように定義abc()すると、派生クラスで再定義するのを簡単に忘れて、問題なく関数を呼び出すことができるため、すべてが大丈夫であると考えることができます。純粋な仮想関数を実装するための良い方法は、この記事にあります。これは、関数に「純粋な仮想関数が呼び出された」と表示させ、プログラムをクラッシュさせることです。
HelloGoodbye 2012

1
私は同じエラーを抱えていました。「lib」への参照の順序を変更すると役立つ場合があることを発見しました。問題のlibを
最初

2
GAH。これは、@ dhardyのコメントを読んで自分に「Doh」と言うために、このページに正確に移動した2回目です。45分間を費やして、おかしな動作を追跡しました= 0;
dwanderson

回答:


223

考えられる理由の1つは、仮想関数を定義せずに宣言しているためです。

同じコンパイル単位で定義せずに宣言すると、それは別の場所で定義されていることを示します。これは、リンカーフェーズが他のコンパイル単位(またはライブラリ)の1つでそれを見つけようとすることを意味します。

仮想関数を定義する例は次のとおりです。

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

この場合、宣言に定義をアタッチしています。つまり、リンカは後で定義を解決する必要はありません。

この線

virtual void fn();

fn()それを定義せずに宣言し、あなたが尋ねたエラーメッセージを引き起こします。

それはコードに非常に似ています:

extern int i;
int *pi = &i;

これは、整数iがリンク時に解決する必要がある別のコンパイル単位で宣言されているpiことを示しています(それ以外の場合は、そのアドレスに設定できません)。


28
それがvirtual void fn() = 0定義だと言うのは誤りです。それは定義ではなく、単なる宣言です。リンカがそれを解決しようとしない唯一の理由は、対応するVMTエントリが関数本体を参照しないことです(ほとんどの場合nullポインタが含まれます)。ただし、完全修飾名を使用するなど、非仮想的な方法でこの純粋な仮想関数を呼び出すことを禁止する人はいません。この場合、リンカ本体検索し、関数を定義する必要があります。そして、はい、純粋な仮想関数の本体を定義できます。
AnT

1
また、純粋な仮想関数の本体を宣言する必要がある場合もあります。
マーク

3
コンパイラ(g ++)は、不足しているシンボルを教えてくれます。注:ダイナミックライブラリリンクの場合、名前が壊れる可能性があります。c ++ filt <mangledNameVariable>を使用して、読み取り可能な形式で取得します。私の場合、クラス名のtypeinfoエラーは、一部の基本クラスで仮想デストラクタの実装が欠落していることが原因でした。
chmike 2013

1
質問では、欠落しているのはtypeinfoであり、rttiに関係していると具体的に述べています。stackoverflow.com/questions/11904519/…の
wilsonmichaelpatrick

1
@gbmhunter、十分に公正です。変更を加えました。
paxdiablo 2014

150

これは、ミックス-fno-rttiして-frttiコーディングするときにも発生する可能性があります。次にtype_info-frttiコードでアクセスされるすべてのクラスが、でコンパイルされたキーメソッドを持っていることを確認する必要があります-frtti。このようなアクセスは、クラスのオブジェクトを作成したり、使用しdynamic_castたりするときに発生する可能性があります。

[ ソース ]


20
どうもありがとうございます。これで、5時間検索した後の問題が解決しました。
11

1
ソースリンクは死んでいる、それは確かにpermalink.gmane.org/gmane.comp.gcc.help/32475
math

1
これを指摘してくれてありがとう。元のページには、まだここに提供されています:web.archive.org/web/20100503172629/http://www.pubbs.net/201004/...
Sergiy Belozorov

3
StackOverflow.comが再び助けになります!一回以上賛成できればいいのに。キーボードで頭を1時間叩いた後、あなたの答えが必要でした。
spartygw 2013

1
n + 1の命が救われ、現在も数えられています:)
ガブリエル

53

これは、宣言された(純粋でない)仮想関数のボディが欠落している場合に発生します。クラス定義では、次のようになります。

virtual void foo();

定義する必要があります(インラインまたはリンクされたソースファイル内):

virtual void foo() {}

または純粋仮想を宣言しました:

virtual void foo() = 0;

27

gccマニュアルから引用:

ポリモーフィッククラス(仮想関数を持つクラス)の場合、type_infoオブジェクトはvtableとともに書き出されます[...]他のすべてのタイプの場合、type_infoオブジェクトが使用されるときに書き出されます。式に「typeid」を適用すると、オブジェクトをスローするか、catch句または例外仕様で型を参照します。

そして、同じページの少し前に:

クラスが非インライン、非純粋仮想関数を宣言している場合、最初の関数がクラスの「キーメソッド」として選択され、vtableはキーメソッドが定義されている翻訳単位でのみ出力されます。

そのため、このエラーは、他の回答がすでに述べたように、「キーメソッド」の定義が欠落している場合に発生します。


2
私の場合、純粋な仮想ではない仮想メソッドを宣言しましたが定義しなかった基本クラスがありました。私がそれらを純粋な仮想にすると、これが私が意味することであり、リンカーエラーはなくなりました。
Tatiana Racheva

@TatianaRachevaありがとう!リンカからのエラー報告は役に立たず、大規模なインターフェースの場合、「= 0」の欠落を見逃しがちです。純粋な仮想のために!
rholmes

21

.soを別の.soにリンクする場合、もう1つの可能性は、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」を使用しないことです。これはコンパイラとリンカにとって複雑なことであり、コードのパフォーマンスを損なう可能性があります。


1
基本クラスが抽象または未使用の場合、基本クラスをエクスポート(再表示)する必要はありません。非仮想関数のみ、通常はコンストラクタのみです。派生一方のクラスは、彼らが使用している場合は、エクスポートする必要があります。
Chris Huang-Leaver

ハックのように感じますが、私の側の症状を解決しました。よろしくお願いします!
マラット14

16

上記の答えは正しいですが、このエラーは仮想関数を持たないクラスのオブジェクトでtypeidを使用しようとした場合にも発生する可能性があります。C ++ RTTIにはvtableが必要であるため、型の識別を実行するクラスには少なくとも1つの仮想関数が必要です。

型情報が仮想関数を実際に必要としないクラスで機能するようにしたい場合は、デストラクタを仮想にします。


2
これがその特定のエラーメッセージの原因である可能性が高いと思うため、更新されました(未定義メソッドのより一般的なケースとは対照的に...)
Alastair

4
私がSOに慣れる必要があったのは、投票に基づいて順序が変わる可能性があるため、「上記の」回答に言及していないことです。他の回答も削除される可能性があるため、通常、ここでは言及しません。私の信念は、答えはスタンドアロンである必要があるということです。ただし、帰属についてはまだユーザー名を参照しています。
paxdiablo 2008年

typetableはvtableなしで使用できます。gccマニュアルからの引用については、私の回答を参照してください。
CesarB 2008年

11

私はこのエラーに数時間費やしましたが、ここでの他の回答は何が起こっているのかを理解するのに役立ちましたが、それらは私の特定の問題を解決しませんでした。

私は両方を使用してコンパイルしたプロジェクトに取り組んでいますclang++g++。を使用してリンクの問題はありませんclang++でしたが、でundefined reference to 'typeinfo forエラーが発生していましたg++

重要なポイント:順序の重要性とのリンクg++。リンクするライブラリを間違った順序でリストすると、typeinfoエラーが発生する可能性があります。

/ とのリンク順序の詳細については、このSOの質問を参照してください。gccg++


ありがとうございました!!!私はこのエラーが発生する理由を見つけるために1日以上費やしましたが、この返信とリンク先の返信を見るまで何も機能しませんでした。本当にありがとう!!
アイリーン、

10

RTTIおよび非RTTIライブラリを扱うコードの可能な解決策:

a)-frttiまたは-fno-rttiのいずれかを使用してすべてを再コンパイルします。b
)a)が不可能な場合は、以下を試してください。

libfooがRTTIなしで構築されていると仮定します。コードはlibfooを使用し、RTTIでコンパイルします。virtualsを持つlibfooでクラス(Foo)を使用すると、リンク時のエラーが発生する可能性があります。クラスFooのtypeinfoがありません。

仮想を持たず、使用するFooに呼び出しを転送する別のクラス(FooAdapterなど)を定義します。

RTTIを使用せず、libfooシンボルにのみ依存する小さな静的ライブラリでFooAdapterをコンパイルします。ヘッダーを提供し、コード(RTTIを使用)で代わりに使用します。FooAdapterには仮想関数がないため、typeinfoはなく、バイナリをリンクできます。libfooのさまざまなクラスをたくさん使用する場合、このソリューションは便利ではないかもしれませんが、それは出発点です。


RTTI設定が異なるライブラリにリンクするのはこれで終わりです。

6

上記のRTTI、NO-RTTIの説明と同様に、この問題は、dynamic_castを使用していて、クラス実装を含むオブジェクトコードを含めなかった場合にも発生する可能性があります。

CygwinでビルドしてからコードをLinuxに移植するこの問題に遭遇しました。makeファイル、ディレクトリ構造、およびgccバージョン(4.8.2)も両方のケースで同じでしたが、コードはCygwinで正しくリンクおよび動作しましたが、Linuxではリンクできませんでした。Red Hat Cygwinは、オブジェクトコードのリンク要件を回避するコンパイラ/リンカーの変更を行っているようです。

Linuxリンカーのエラーメッセージは、適切にdynamic_cast行に誘導しましたが、このフォーラムの以前のメッセージでは、実際の問題ではなく、不足しているオブジェクトのコードではなく、不足している関数の実装を探していました。私の回避策は、dynamic_castを使用するのではなく、ベースおよび派生クラスの仮想型関数(たとえば、仮想int isSpecialType())を置き換えることでした。この手法により、dynamic_castが適切に機能するようにするためだけにオブジェクト実装コードをリンクする必要がなくなります。


5

基本クラス(抽象基本クラス)では、仮想デストラクタを宣言します。デストラクタを純粋な仮想関数として宣言することはできないため、抽象クラスでここで定義する必要があります。virtual〜base( ){}は、または派生クラスのいずれかで行います。

これを行わないと、リンク時に「未定義のシンボル」になります。VMTには、派生クラスの実装に応じてテーブルを更新するときに、一致するNULLを持つすべての純粋仮想関数のエントリがあるためです。ただし、純粋ではないが仮想関数の場合は、VMTテーブルを更新できるように、リンク時に定義が必要です。

c ++ filtを使用してシンボルをデマングルします。$ c ++ filtのように、_ZTIN10storageapi8BaseHostEは「typeinfo for storageapi :: BaseHost」のようなものを出力します。


3

たった今、これらのエラーがたくさん発生しました。何が起こったのか、ヘッダーファイルのみのクラスをヘッダーファイルとcppファイルに分割しました。ただし、ビルドシステムを更新しなかったため、cppファイルはコンパイルされませんでした。ヘッダーで宣言されているが未実装の関数への未定義の参照があるだけで、これらのtypeinfoエラーがたくさん発生しました。

解決策は、ビルドシステムを再実行して、新しいcppファイルをコンパイルおよびリンクすることでした。


3

私の場合、ヘッダーファイルなどを含むサードパーティのライブラリを使用しました。1つのクラスをサブクラス化し、サブクラスをインスタンス化しようとすると、このようなリンクエラーが発生しました。

@sergiyによって言及されたように、「rtti」の問題である可能性があることを知っていたので、コンストラクターの実装を別の.cppファイルに入れ、「-fno-rtti」コンパイルフラグをファイルに適用することで回避できました。それはうまくいきます。

このリンクエラーの内部についてはまだはっきりしていませんので、私の解決策が一般的かどうかはわかりません。ただし、@ francoisで言及されているアダプターの方法を試す前に、一撃の価値があると思います。もちろん、すべてのソースコードが利用できる場合(私の場合ではありません)、可能であれば '-frtti'で再コンパイルすることをお勧めします。

もう1つ、私のソリューションを試すことを選択した場合は、別のファイルをできるだけ単純にして、C ++のいくつかの凝った機能を使用しないようにしてください。ブーストに関連することに特に注意してください。原因の多くはrttiに依存しています。


2

インターフェース(すべての仮想関数を含む)にもう1つの関数が必要で、「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): `typeinfo for ICommProvider 'への未定義の参照

からの簡単な変更

virtual int vaClose();

virtual int vaClose() = 0;

問題を修正しました。それが役に立てば幸い


1

まれな状況に遭遇しましたが、これは同様の状況にある他の友人を助けるかもしれません。古いシステムでgcc 4.4.7を使用する必要があります。c ++ 11以上のサポートでコードをコンパイルする必要があるため、gcc 5.3.0の最新バージョンをビルドします。コードをビルドし、依存関係が古いコンパイラでビルドされている場合に依存関係にリンクすると、リンクパスを-L / path / to / lib -llibnameで明確に定義したにもかかわらず、「undefined reference to」エラーが発生しました。cmakeでビルドされたboostやプロジェクトなどの一部のパッケージは、通常、古いコンパイラーを使用する傾向があり、通常はそのような問題を引き起こします。彼らが新しいコンパイラを使用するようにするためには、長い道のりが必要です。


1

私の場合、たとえdynamic_cast呼び出しがあっても、それは純粋にライブラリ依存関係の問題です。makefileに十分な依存関係を追加した後、この問題はなくなりました。


0

依存関係がなしでコンパイルされていることを確認してください-f-nortti

一部のプロジェクトでは、RocksDBのように、明示的に設定する必要があります。

USE_RTTI=1 make shared_lib -j4

0

私の場合、それは純粋な仮想として定義されていないインターフェイスクラスの仮想関数でした。

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

ちょっと忘れました= 0

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.