次のエラーに出くわしました(オンラインで解決策を見つけましたが、スタックオーバーフローにはありません):
(.gnu.linkonce.[stuff]): [メソッド] [オブジェクト ファイル] への未定義参照:(.gnu.linkonce.[stuff]): `typeinfo for [classname]' への未定義参照
これらの「typeinfo への未定義の参照」リンカ エラーのいずれかが発生するのはなぜですか?
(舞台裏で何が起こっているかを説明できればボーナスポイントです。)
考えられる理由の1つは、仮想関数を定義せずに宣言しているためです。
同じコンパイルユニットで定義せずに宣言すると、別の場所で定義されていることを示します。これは、リンカーフェーズが他のコンパイルユニット(またはライブラリ)の1つでそれを見つけようとすることを意味します。
仮想関数を定義する例は次のとおりです。
virtual void fn() { /* insert code here */ }
この場合、宣言に定義を付加します。これは、リンカが後でそれを解決する必要がないことを意味します。
この線
virtual void fn();
それを定義せずに宣言しfn()、あなたが尋ねたエラーメッセージを引き起こします。
これはコードと非常によく似ています。
extern int i;
int *pi = &i;
これは、整数iがリンク時に解決される必要がある別のコンパイル単位で宣言されていることを示しています(そうでない場合pi、そのアドレスに設定することはできません)。
-fno-rttiこれは、混合してコーディングするときにも発生する可能性があり-frttiます。type_info次に、コード内でアクセスされるすべてのクラス-frttiのキー メソッドが .xml でコンパイルされていることを確認する必要があります-frtti。dynamic_castこのようなアクセスは、クラスのオブジェクトを作成したり、使用したりするときに発生する可能性があります。
[ソース]
これは、宣言された(純粋でない)仮想関数に本体がない場合に発生します。クラス定義では、次のようになります。
virtual void foo();
定義する必要があります(インラインまたはリンクされたソースファイルで):
virtual void foo() {}
または純粋な仮想を宣言しました:
virtual void foo() = 0;
gccマニュアルからの引用:
ポリモーフィック クラス (仮想関数を持つクラス) の場合、type_info オブジェクトは vtable と共に書き出されます [...] 他のすべての型の場合、使用時に type_info オブジェクトを書き出します: `typeid' を式に適用する場合、オブジェクトをスローするか、catch 句または例外仕様で型を参照します。
そして、同じページの少し前に:
クラスが非インライン、非純粋仮想関数を宣言する場合、最初の関数がクラスの「キー メソッド」として選択され、vtable はキー メソッドが定義されている変換単位でのみ生成されます。
したがって、このエラーは、他の回答が既に述べたように、「キーメソッド」に定義がない場合に発生します。
1 つの .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」を使用しないことです。これは、コンパイラとリンカの処理を複雑にし、コードのパフォーマンスを損なう可能性があります。
前の答えは正しいですが、このエラーは、仮想関数を持たないクラスのオブジェクトでtypeidを使用しようとした場合にも発生する可能性があります。C ++ RTTIにはvtableが必要であるため、型の識別を実行するクラスには、少なくとも1つの仮想関数が必要です。
仮想関数を実際に必要としないクラスで型情報を機能させたい場合は、デストラクタを仮想にします。
このエラーに数時間費やしましたが、ここでの他の回答は何が起こっているのかを理解するのに役立ちましたが、特定の問題は解決しませんでした.
clang++と の両方を使用してコンパイルするプロジェクトに取り組んでいますg++。を使用してリンクの問題は発生していませんでしclang++たが、 でundefined reference to 'typeinfo forエラーが発生していましたg++。
重要なポイント:オーダー MATTERS を にリンクしg++ます。リンクするライブラリを間違った順序でリストすると、typeinfoエラーが発生する可能性があります。
/を使用した順序のリンクの詳細については、この SO の質問を参照してください。gccg++
RTTI および非 RTTI ライブラリを扱うコードの可能な解決策:
a) -frtti または -fno-rtti のいずれかを使用してすべてを再コンパイルします
b) a) ができない場合は、次のことを試してください。
libfoo が RTTI なしでビルドされていると仮定します。コードは libfoo を使用し、RTTI でコンパイルします。バーチャルを持つ libfoo でクラス (Foo) を使用すると、「クラス Foo の typeinfo がありません」というリンク時エラーが発生する可能性があります。
仮想を持たず、使用する Foo への呼び出しを転送する別のクラス (FooAdapter など) を定義します。
RTTI を使用せず、libfoo シンボルのみに依存する小さな静的ライブラリで FooAdapter をコンパイルします。それにヘッダーを提供し、代わりにそれをコードで使用します (RTTI を使用します)。FooAdapter には仮想関数がないため、typeinfo はなく、バイナリをリンクできます。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 行に誘導しましたが、このフォーラムの以前のメッセージでは、実際の問題ではなく、関数の実装の欠落を探していました: オブジェクト コードの欠落。私の回避策は、dynamic_cast を使用するのではなく、仮想 int isSpecialType() などの基本クラスと派生クラスで仮想型関数を置き換えることでした。この手法により、dynamic_cast を適切に機能させるためだけにオブジェクト実装コードをリンクする必要がなくなります。
私は今、これらのエラーをたくさん受け取りました。何が起こったかというと、ヘッダー ファイルのみのクラスをヘッダー ファイルと cpp ファイルに分割したことです。ただし、ビルド システムを更新していないため、cpp ファイルはコンパイルされませんでした。ヘッダーで宣言されているが実装されていない関数への未定義の参照があるだけで、これらの typeinfo エラーがたくさん発生しました。
解決策は、ビルド システムを再実行して、新しい cpp ファイルをコンパイルおよびリンクすることでした。
私の場合、ヘッダー ファイルなどのサードパーティ製ライブラリを使用しました。1 つのクラスをサブクラス化し、サブクラスをインスタンス化しようとすると、このようなリンク エラーが発生しました。
@sergiyが述べたように、「rtti」の問題である可能性があることを知っていたので、コンストラクターの実装を別の.cppファイルに入れ、「-fno-rtti」コンパイルフラグをファイルに適用することで回避できました。それはうまくいきます。
このリンクエラーの内部についてはまだはっきりしていないので、私の解決策が一般的かどうかはわかりません。ただし、@ francois で言及されているように、アダプターの方法を試す前に試してみる価値があると思います。もちろん、すべてのソース コードが利用可能であれば (私の場合ではありません)、可能であれば「-frtti」を指定して再コンパイルすることをお勧めします。
もう 1 つ、私の解決策を試す場合は、別のファイルをできるだけシンプルにして、C++ の派手な機能を使用しないでください。ブースト関連のものには特に注意してください。その多くは rtti に依存するためです。
インターフェイス (すべての純粋仮想関数を含む) がもう 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): 「ICommProvider の typeinfo」への未定義の参照
からの簡単な変更
virtual int vaClose();
に
virtual int vaClose() = 0;
問題を修正しました。それが役に立てば幸い
In my case it is purely a library dependency issue even if I have dynamic_cast call. After adding enough dependency into makefile this problem was gone.
依存関係が なしでコンパイルされたことを確認してください-f-nortti。
一部のプロジェクトでは、RocksDB のように明示的に設定する必要があります。
USE_RTTI=1 make shared_lib -j4