5

「vtableへの未定義参照」エラーメッセージを扱うスタックオーバーフローに関する他の質問があります。次のコードは、引数なしのコンストラクター C() がインラインで実装されているかどうかに応じて、コンパイルされるか、コンパイルされません。メンバー関数 m() は純粋な仮想でなければならず、問題を修正するために行う正しい変更であることはわかっています。私にとって紛らわしいのは、明らかに無関係な変更でコンパイルできることです。

次のコードは、g++ (ubuntu 64 ビットの 4.6.3) でコンパイルされず、予期される「C の vtable への未定義参照」メッセージを生成します (これは、問題が m() にあることを考えると、依然としてひどいエラー メッセージです)。

Header.h

#ifndef HEADER_H
#define HEADER_H

class C
{
  public:
    C();
    virtual void m();
};

#endif

実装.cpp

#include "Header.h"
C::C() {}

メイン.cpp

#include "Header.h"
int main()
{
   return 0;
}

次の無関係な変更により、コンパイルが可能になります。

  • C::C() の非インライン実装を Implementation.cpp から削除
  • C() の単純なインライン実装を Header.h のクラスに追加します。

なぜこれがコンパイルを許可するのですか? これはコンパイラのバグですか、オプティマイザの問題ですか、それとも標準的な驚きの暗い隅ですか?

4

1 に答える 1

7

これはコンパイラのバグですか、オプティマイザの問題ですか、それとも標準的な驚きの暗い隅ですか?

上記のどれでもない。これはバグではなく、最適化とは関係ありません。この特定の問題は標準の範囲外であり、関連するABIでカバーされています(これは事実上の標準にすぎません)。

C::mキー関数であり、どこにも定義していません。つまり、コンパイラは vtable を出力しません。

コードがこれらの変更でコンパイルされるのには、(複雑ではあるが) 適切な理由があります。

  • C::C() の非インライン実装を Implementation.cpp から削除

ABI ドキュメントの2.6で説明されているいくつかの複雑な理由により、構築中に vtable が必要になります。そのため、コンストラクターの定義は vtable への参照を作成します。リンカーは、リンク時に欠落していると通知します。コンストラクターの定義を削除すると、vtable への参照がなくなります。

  • C() の単純なインライン実装を Header.h のクラスに追加します。

特定の翻訳単位で呼び出されないインライン関数はオブジェクト ファイルに出力されないため、関数をインラインにすることは、コンストラクターがオブジェクト ファイルにないことを意味し、オブジェクト ファイルは vtable を参照しません。リンカーはリンク時にそれを探す必要はありません。

インライン コンストラクターが実際に使用されるようにプログラムを変更すると (たとえば、Cinを作成することによりmain)、同じリンカー エラーが再び発生します。これは、インライン コンストラクターが で定義されMain.o、vtable が必要になるためです。

class C
{
  public:
    C() { }  // inline
    virtual void m();
};

int main()
{
    C c;
}
于 2012-08-23T13:34:56.430 に答える