std::vector に前方宣言を使用できないのはなぜですか?

StackOverflow https://stackoverflow.com/questions/37346

  •  09-06-2019
  •  | 
  •  

質問

次のようなクラスを作成すると:

// 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 つありますが、実際にはそれぞれ異なります。

  1. B へのポインタを使用する
  2. 軽量なものを使用する プロキシ 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年代ですね…;)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top