質問
次のようなクラスを作成すると:
// B.h
#ifndef _B_H_
#define _B_H_
class B
{
private:
int x;
int y;
};
#endif // _B_H_
そして次のように使用します:
// main.cpp
#include <iostream>
#include <vector>
class B; // Forward declaration.
class A
{
public:
A() {
std::cout << v.size() << std::endl;
}
private:
std::vector<B> v;
};
int main()
{
A a;
}
コンパイラがコンパイル時に失敗する main.cpp
. 。今、私が知っている解決策は、 #include "B.h"
, しかし、なぜ失敗するのか気になります。どちらでもない g++
または cl
この件に関しては、 のエラー メッセージが非常に啓発的でした。
解決
コンパイラは、適切なレイアウト情報を生成する前に、「B」の大きさを知る必要があります。代わりにあなたが言ったら std::vector<B*>
, の場合、コンパイラーはポインターの大きさを知っているため、B の大きさを知る必要がありません。
他のヒント
実際、あなたの例は、A のコンストラクターが B の型を知っているコンパイル ユニットに実装されている場合にビルドされます。
他の人が前に述べたように、std::vector インスタンスには T へのポインターのみが含まれるため、T が何であっても固定サイズです。ただし、ベクトルのコンストラクターは具体的な型に依存します。A() がベクトルの ctor を呼び出そうとするため、この例はコンパイルされません。これは B が分からないと生成できません。うまくいくものは次のとおりです。
Aさんの宣言:
// A.h
#include <vector>
class B; // Forward declaration.
class A
{
public:
A(); // only declare, don't implement here
private:
std::vector<B> v;
};
A の実装:
// A.cpp
#include "A.h"
#include "B.h"
A::A() // this implicitly calls vector<B>'s constructor
{
std::cout << v.size() << std::endl;
}
ここで、A のユーザーは、B ではなく A のみを知る必要があります。
// main.cpp
#include "A.h"
int main()
{
A a; // compiles OK
}
A::v をインスタンス化するには、コンパイラーは B の具体的な型を知る必要があります。
コンパイル時間を短縮するために #include の荷物の量を最小限に抑えようとしている場合、実行できることが 2 つありますが、実際にはそれぞれ異なります。
- B へのポインタを使用する
- 軽量なものを使用する プロキシ Bへ
必要なのは B のサイズだけではありません。最新のコンパイラは、たとえば、可能な場合は memcpy を使用してベクトルのコピーを高速化するための巧妙なトリックを備えています。これは通常、要素タイプの POD 性に部分的に特化することで実現されます。B が POD であるかどうかは、前方宣言からはわかりません。
fyzix が言ったように、前方宣言が機能しない理由はインライン コンストラクターにあります。空のコンストラクターであっても、非 POD メンバーの構築など、多くのコードが含まれる場合があります。あなたの場合、初期化するベクトルがありますが、テンプレートのタイプを完全に定義しないと初期化できません。
デストラクタについても同様です。ベクターには、保持しているインスタンスを破棄するときにどのデストラクターを呼び出すかを指示するテンプレート タイプ定義が必要です。
この問題を解決するには、コンストラクターとデストラクターをインライン化しないだけです。B が完全に定義された後のどこかで、それらを個別に定義します。
詳細については、http://www.chromium.org/developers/coding-style/cpp-dos-and-donts
これは、ベクトルを使用するか、単に 1 つの B をインスタンス化しようとするかに関係ありません。インスタンス化にはオブジェクトの完全な定義が必要です。
おい、インスタンス化してるんだよ std::vector
不完全なタイプです。前方宣言には触れず、コンストラクターの定義を次の場所に移動するだけです。 .cpp
ファイル。
前方宣言が使用できない理由は、B のサイズが不明であるためです。
あなたの例では、A.h の中に B.h を含めることができない理由はありません。では、実際に解決しようとしている問題は何ですか?
編集: この問題を解決する別の方法もあります。C/C++ の使用をやめてください。まさに1970年代ですね…;)