1

テンプレートを使用してアイテムのstd:listを取得しようとしています。各アイテムには、それを含むリストへのポインターがありますが、コンパイラメッセージが表示され続けます。

これは非常に簡略化されたバージョンのコードです。

template <class E> class Item  
{
    public:
        E* owner; // pointer to list that owns us.
};

template <class E> class BaseList: public std::list<E>
{
protected:
    typedef std::list<E> inherited;
public:

    void push_back(const E &e)
    {
        E tmp(e);
        tmp.owner = this;  // This line gives the error.
        inherited::push_back(tmp);
    }
};

class MyList;

class MyItem : public Item<MyList>
{
};

class MyList : public BaseList<MyItem>
{
};

void foo()  // test code to instantiate template
{
    MyList l;
    MyItem m;
    l.push_back(m);
}

しかし、私のコンパイラは次の行でバーフします:-

        tmp.owner = this;  

エラーは次のとおりです。

[BCC32 Error] Unit7.cpp(30): E2034 Cannot convert 'BaseList<MyItem> * const' to 'MyList *'

「これ」がどういうわけかconstになったようですが、理由がわかりません。コンパイラはCodegearC++Builder2009です。

テンプレートを使用することに100%満足しているわけではないことを認めているので、これが私の問題なのかコンパイラなのかわかりません。テンプレートを使用しない同じコードは正常にコンパイルされますが、このように機能させたいアイテム/リストクラスがいくつかあるため、明らかにそれは私が望むものではありません。

また、各アイテムにすべての「所有者」ポインタが含まれないようにするためのより良い手法はありますか?

編集:私は例をあまりにも削除したと思います:「MyList」は実際に新しいメソッドを導入し、「MyItem」は「owner」ポインターを介してアクセスする必要があります。

概要:すべてのコメントと回答に感謝します。受け入れられた答えが言うように、問題は単にBaseListへのポインターとMyListの間の型の非互換性の1つです。

STLコンテナと代替設計から派生することに関して提起された問題も役立ちますが、私が使用したソリューションは、以下のLucTourailleのものと本質的に同じです。

4

8 に答える 8

8

30行目で、「this」はBaseList<MyIteM>MyListではなく、へのポインタです。クラスを派生クラスに置き換えることはできますが、その逆はできません。

BaseList<MyItem>次のように、typedefMyListをにすることができます。

typedef BaseList<MyItem> MyList

または、代わりにMyItemを派生させItem<BaseList<MyItem> >ます。

タイプから派生する場合は、別のタイプを作成します。typedefを実行するときは、そのタイプのエイリアスを作成します。したがって、typedefを実行すると、コンパイラはこれを受け入れます。

于 2009-05-27T11:24:43.273 に答える
4

あなたがすでに持っている答えに加えて、標準ライブラリコレクションクラスは仮想デストラクタを持たず、それらのメンバー関数のどれも仮想ではないので、派生することを意図していないことも指摘しておきます。

于 2009-05-27T11:30:10.877 に答える
3

あるべきではありませんtmp.owner = static_cast<MyList*>(this)。EのタイプはMyItemのMyListであるため、E*はMyList*になります。このポインターの型はBaseList*になるため、コンパイラーは、基本クラスのポインターを派生クラスのポインターに変換できないというエラーを出します。

于 2009-05-27T11:28:08.057 に答える
2

あなたがそれが何を必要としているのかを言わないとき、より良い解決策があるかどうかを言うのは難しいです。各要素に、それらが格納されているリストへのポインタが必要なのはなぜですか?

とにかく、標準のコンテナから継承すると、悪いことが起こる可能性があります。仮想デストラクタがないため、十分に注意する必要があります。

より良い解決策は、push_backを実行する無料の関数を提供することです。

template <typename T>
void push_back(std::list<T>& list, const T& t) {
        T tmp(t);
        tmp.owner = this;
        list.push_back(tmp);
}

非仮想デストラクタの問題を回避するだけでなく、リストのタイプが1つしかないため、コンパイラエラーも解決します。

もちろん、そもそもなぜこの所有者ポインターが必要なのかがわかっている場合は、さらに優れたソリューションが存在する可能性があります。

編集

編集とコメントに応じて、継承ではなく構成を使用します。

struct House {
  std::string zipcode;
  std::list<Person> persons;

  void AddPerson(const Person& person) {
    Person tmp(person);
    tmp.owner = this; // The owner field should be a house, not the list of persons.
    persons.push_back(tmp);
  }
};

家が人のリストを保存しているときに得られるほぼ循環参照では販売されていませんが、人はその家へのポインタを持っています。

これらのクラスを可能な限り分離したいと思います。人に手紙を送りたい場合は、SendLetter(Person、House)に電話します。その家のこの人に手紙を送ってください。

于 2009-05-27T11:37:57.273 に答える
1

ちなみに、stdからクラスを拡張しないでください。それらは、std用に構築されていません。

具体的には、仮想デストラクタがないため、基本クラスへのポインタでdeleteを呼び出すと、派生クラスのデストラクタが呼び出されることはありません。

あなたはそれについてもっと読むことができますユーザー定義のメソッドでC++STLコンテナを拡張するより良い方法に関するアドバイス

于 2009-05-27T11:39:25.087 に答える
1

私はjalfの無料関数のアイデアが好きです。私はそれを作ります:

template <class X, class Y> // X must have a push_back(Y) member, Y must have an X* owner member
void push_back(X& container, Y value)
{
    value.owner = container;
    container.push_back(value);
}

これは、渡されたXが

  • コンテナ自体、
  • 元のコードと同様にコンテナから派生します
  • またはコンテナを含み、転送push_back関数を持っています
于 2009-05-27T12:05:14.380 に答える
1

すでに指摘されているように、愛情

tmp.owner = this;

thisと同じタイプがないため、失敗しますtmp.owner。1つの解決策はキャストを実行することですが、そのためには、BaseListにコンテナータイプを指定する必要があります。これは、Itemのtypedefを使用して実行できます。コードは次のとおりです。

template <class Item> class BaseList
{      
  public:

    void push_back(Item i)
    {
        i.owner = static_cast<Item::containerType *>(this); // note the cast
        items.push_back(i);
    }

    Item & back() { return items.back(); }

  protected:
    std::list<Item> items;
};


template <class Container> class Item
{
    public:
        typedef Container containerType; // Typedef used by BaseList

        containerType* owner; // pointer to list that owns us. 
};

私はまた、次の公的な派生を削除しましたstd::list:多くの人が言ったように、これは(ほとんどの場合)避けるのが最善です; 構成、またはおそらくプライベート継承の使用を検討する必要があります。

PS:アイテムのownerプライベートとBaseList<Item>::push_back友達を作ってみましたが、うまくいきませんでした。それは可能ですか?(コメントで答えるには長すぎる場合は、遠慮なく質問して答えてください)

于 2009-05-27T16:46:21.363 に答える
1

に関してconstコンパイラが言及するタイプBaseList<MyItem> * constは赤いニシンです-それはオブジェクトへconstのポインタで constなく、つまり、変更されないアドレスであるポインタです。(あなたがそれについて考えるとき、this他の何かを指すように変わることは決してありませんか?)

于 2009-05-27T19:47:06.380 に答える