8

簡単な質問: どうすればこれを機能させることができますか?

struct A {
    double whatever; 
    std::unordered_map<std::string, A> mapToMoreA; 
}

g++ エラー: std::pair<_T1, _T2>::second の型が不完全です

私が理解している限り、マップをインスタンス化するとき、コンパイラは A のサイズを知る必要がありますが、マップは A の宣言で宣言されているため、コンパイラはこれを知りません。これを回避してポインタを使用する唯一の方法です。あ(やりたくない)?

4

7 に答える 7

6

ほとんどの場合、コンテナの実装の詳細 (より正確には、コンテナ宣言の時点で何がインスタンス化され、何がインスタンス化されないか) に依存します。どうやら、std::unordered_map実装には型が完全である必要があります。同時に、GCC の実装はstd::map、不完全な型で完全に問題なくコンパイルされます。

このような違いの原因を説明するために、次の例を検討してください。のような機能の独自の単純な実装を作成することに決めstd::vector、次のようにベクター クラスを宣言したとしましょう。

template <typename T> class my_vector {
  T *begin;
  T *end;
  ...
};

クラス定義に へのポインターのみが含まれている限りT、型Tはクラス定義自体に対して完全である必要はありません。問題なくmy_vector不完全なインスタンスを作成できますT

class X;
my_vector<X> v; // OK

型の「完全性」は、後での個々のメソッドを使用する (したがってインスタンス化する) ときに必要になりますmy_vector

ただし、何らかの理由で の直接インスタンスをTベクター クラスに含めることにした場合、事態は変化します。

template <typename T>
class my_vector {
  T *begin;
  T *end;
  T dummy_element;
  ...
};

の完全性は、それ自体Tのインスタンス化の時点で、非常に早い段階で必要になります。my_vector

class X;
my_vector<X> v; // ERROR, incomplete type

あなたの場合、そのようなことが起こっているに違いありません。unordered_mapあなたが扱っているの定義には、何らかの形で の直接インスタンスが含まれていますA。これが、インスタンス化が不可能な理由です (明らかに、その場合、無限に再帰的な型になってしまいます)。

の実装を通じてよりよく考えれば、それ自体を直接のメンバーとしてunordered_map含めないようにすることができます。Aそのような実装はA完全である必要はありません。ご指摘のとおり、Boost の実装はunordered_map、この点でより適切に設計されています。

于 2012-07-11T18:45:21.797 に答える
2

Boost.Variantには、この目的のために明示的に便利なユーティリティがあります – boost::recusive_wrapper<>. 以下が機能するはずです。

struct A {
    double whatever; 
    std::unordered_map<std::string, boost::recursive_wrapper<A>> mapToMoreA; 
};

唯一の注目すべき欠点は、Boost.Variant がまだ C++11 移動セマンティクスをサポートするように更新されていないことです。更新: Boost 1.56 で追加されました。

于 2012-07-11T19:07:45.877 に答える
2

不完全な型で動作するスマート ポインター以外の STL コンテナーを知りません。ただし、ポインターを使用したくない場合は、ラッパー構造体を使用できます。

struct A {
    struct B { double whatever; }; 
    std::unordered_map<std::string, B> mapToB; 
};

編集:上記がユースケースを満たさない場合の代替ポインターです。

struct A {
    double whatever;
    std::unordered_map<std::string, std::unique_ptr<A>> mapToMoreA; 
};

boost::unordered_map不完全な型をサポートするだけでなく、Visual Studio ではるかに優れたデバッグ パフォーマンスを備えたものを使用することもできます。Microsoft の の実装は、std::unordered_mapイテレータのデバッグ チェックが過剰なために非常に非効率的です。どちらのコンテナでも、gcc のパフォーマンスに関する懸念はありません。

于 2012-07-11T18:36:59.757 に答える
0

C++ では、通常、不完全な型に対して事前定義された定数サイズを持つポインターを使用します。

もちろん、これによりマップの使用方法が変わります。メンバーにアクセスするには*or演算子を使用して逆参照する必要があり、ある時点でポインターにアクセスする必要があります。->delete

struct A
{
    double bla;
    std::map<std::string, A*> mapToMoreA;
};

A のメンバー関数は、ブロック内でプロトタイプに分割し、struct後で実装する必要があります。そうしないと、A とそのメンバーはまだ完全に定義されていません。

struct A
{
    double bla;
    std::map<std::string, A*> mapToMoreA;
    void doStuff(const std::string& str);
};

void A::doStuff(const std::string& str)
{
    mapToMoreA[str] = new A();
}
于 2012-07-11T18:37:49.553 に答える
0

マップにポインターを保持させることが受け入れられない場合は、おそらくこれが機能します。

struct A {
  struct hidden;
  std::unique_ptr<hidden> pimpl;
};

struct A::hidden {
  double whatever;
  std::unordered_map<std::string, A> mapToMoreA;
};
于 2012-07-11T18:42:20.977 に答える
0

または、マップへのポインターを使用します。この場合、ポインターは void* 型でなければなりません (一連の関数の背後に隠すことができます)。不完全な値の型に対処できる std::unordered_map の代替手段があるかもしれません。

于 2012-07-11T18:32:36.873 に答える
-2

定義の前に前方宣言するだけstruct A;で、コンパイラーは満足するはずです。

編集:だから、何度か反対票を投じられた後、何が欠けているかを確認するために次のように書いた:

#include <boost/unordered_map.hpp>                                                                                      
#include <string>
#include <iostream>

struct A;

struct A {
    double whatever;
    boost::unordered_map<std::string, A> mapToMoreA;
};

int main(void)
{
    A b;
    b.whatever = 2.5;
    b.mapToMoreA["abc"] = b;
    std::cerr << b.mapToMoreA["abc"].whatever << std::endl;
    return 0;
}

これは私のMacでg ++ 4.2.1を使用して正常にコンパイルされ、実行時に「2.5」が出力されます(予想どおり)。

申し訳ありませんがunordered_map、ブーストなしではありません。それが問題ですか?(つまり、std::unordered_mapboost よりも多くの制約をコンパイラに課しますか?) そうでなければ、質問についてここで何が欠けているのかわかりません。これに反対票を投じている人は、コメントで教えてください。ありがとう!

于 2012-07-11T18:29:30.697 に答える