web-dev-qa-db-ja.com

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

私は次のエラーに出くわしました(そしてソリューションはオンラインで見つかりましたが、Stack Overflowにはありません):

(.gnu.linkonce。[stuff]):[method]への未定義の参照[object file] :(。gnu.linkonce。[stuff]): `typeinfo for [classname] 'への未定義の参照

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

(舞台裏で何が起こっているかを説明できれば、ボーナスがポイントになります。)

188
cdleary

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

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

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

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

この場合、宣言に定義を添付しているため、リンカーは後でそれを解決する必要はありません。

この線

virtual void fn();

fn()を定義せずに宣言し、要求したエラーメッセージを表示します。

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

extern int i;
int *pi = &i;

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

200
paxdiablo

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

[ ソース ]

137

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

virtual void foo();

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

virtual void foo() {}

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

virtual void foo() = 0;
46
cdleary

gccマニュアル からの引用:

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

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

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

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

26
CesarB

ある.soを別の.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」を使用しないことです。これはコンパイラーとリンカーにとって事態を複雑にし、おそらくコードのパフォーマンスを損ないます。

19
human

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

仮想関数を本当に必要としないクラスで型情報を機能させたい場合は、デストラクタを仮想化します。

15
Tyler McHenry

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から多くの異なるクラスを使用する場合、このソリューションは便利ではないかもしれませんが、それは出発点です。

9
Francois

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

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

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

gcc/g++とのリンク順序の詳細については、 this SO question を参照してください。

8
dinkelk

上記の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を適切に機能させるためだけにオブジェクト実装コードをリンクする必要がなくなります。

6
FNE

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

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

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

5
Prashanth

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

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

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

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

3
uwydoc

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

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

3
Claudiu

インターフェイス(すべての純粋仮想関数)にもう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):「typeCom for ICommProvider」への未定義の参照

からの簡単な変更

virtual int vaClose();

virtual int vaClose() = 0;

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

2
Alex Paniutin

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

1
Charlie

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

1
Kemin Zhou

依存関係が-f-norttiなしでコンパイルされたことを確認します。

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

USE_RTTI=1 make shared_lib -j4
0
Vitaly Isaev