2

C ++(11)でコレクションクラスのような.NETフレームワークを実装しようとしています。私の問題は無効な共変型です。私はこれらのクラスを持っています:

template<typename T>
class IEnumerator
{
public:
    virtual bool MoveNext() = 0;
    //...
};

template<typename T>
class IEnumerable
{
    virtual IEnumerator<T> GetEnumerator() = 0;
};

template<typename T>
class List : public IEnumerable<T>
{
public:
    struct Enumerator : public IEnumerator<T>
    {
        Enumerator(List<T> &list)
        {
            //...
        }
        // ...
    };

    Enumerator GetEnumerator()
    {
        return Enumerator(*this);
    }
};

私によると、これは素晴らしいです。しかし、C++で実装することは不可能に見えます。g ++で「無効な共変リターン型」を取得します。私が読んだ限り、問題は、GetEnumeratorがポインターまたは列挙子への参照のみを返し、列挙子自体のオブジェクトを返さない可能性があることです。

次のようなポインタを返さないようにしたいと思います。

Enumerator *GetEnumerator()
{
    return new Enumerator(*this);
}

発信者にわざわざ削除してほしくないからです。一時オブジェクトを使用すると、オブジェクトは不要になったため、自動的に削除されます。参照の使用はさらに悪い場合があります。

私は何かが足りないのですか?それとも、C ++標準(および言語)に大きな穴がありますか?本当にこういうことを成し遂げたいです。

前もって感謝します。

4

3 に答える 3

5

共変値の戻り型は実装できません。問題は、返されたオブジェクトのためにスタック内のスペースを割り当てるのは呼び出し元の責任であり、共変値の戻りに必要なスペースの量はコンパイル時に不明になることです。

返されるオブジェクトは(実際の派生オブジェクトではなく)ポインターまたは参照であり、サイズはコンパイル時にわかっているため、これはポインター/参照とシームレスに連携します。

@curiousguyとの(私の側では)かなりばかげた議論の後、私は前の答えから戻る必要があります。共変値の戻り型を不可能にする技術的な問題はありません。一方、それは異なる悪影響を及ぼします:

設計の観点から、ベースから呼び出された場合、返されるオブジェクトはスライスする必要があります(これは返されるオブジェクトのサイズが重要な場所です)。これは現在のモデルとの明らかな違いです。現在のモデルでは、関数は常に同じオブジェクトを返します。型を変更するのは参照またはポインターのみです。しかし、実際のオブジェクトは同じです。

一般的なケースでは、共変値型はコピーの省略の最適化の一部を阻害します。現在、値で返される関数の多くの呼び出し規約では、呼び出し元が返されたオブジェクトの場所へのポインターを渡すように指示されています。これにより、呼び出し元は値を保持する変数のスペースを予約し、そのポインターを渡すことができます。次に、呼び出し先はそのポインターを使用して、呼び出し元のコンテキストで値を保持するオブジェクトの代わりに構築でき、コピーは必要ありません。共変値の戻り型を使用し、最終オーバーライドによって作成された最も派生したオブジェクトは、未定義の動作を回避するために破棄する必要があるためです。呼び出し元はメモリ内の場所へのポインタを渡し、トランポリン関数は最終的なオーバーライドの返されたオブジェクト用のスペースを予約する必要があります。その後、次のことを行う必要があります。その2番目のオブジェクトから最初のオブジェクトへのスライスコピー。コピーのコストが発生します。

いずれにせよ、操作の実際のコストは、呼び出しが実行される参照の静的タイプに応じて、最終的なオーバーライドへの呼び出しのセマンティクスが異なるという事実ほど問題にはなりません。。


*これはすでに現在の言語定義に当てはまります。すべての非仮想関数について、派生型がベースのメンバー関数を非表示にする場合、返されるポインター/参照の静的型(仮想関数の呼び出しに使用される静的型に依存します)は、取得する関数に影響します実際に呼び出され、動作が異なります。

于 2012-05-25T18:03:31.197 に答える
1
template<typename T>
class IEnumerable
{
    virtual IEnumerator<T> GetEnumerator() = 0;
};

を返そIEnumerable<T>うとしていますが、これは抽象基本クラスです。つまり、インスタンス化できないクラスのオブジェクトを作成することを約束します。

インスタンス化できるのは、抽象基本クラスから派生した具象クラスのみです。

あなたはおそらくそのようなオブジェクトへのポインタを返すつもりでした。とにかく、これは悪いデザインです。C++でJavaをエミュレートする必要はありません。

于 2012-08-14T21:40:31.633 に答える
0

ポインタを使用していない限り、C++ではそのようなことはしません。.netの人たちは参照を使用するので、ほとんど同じものです。

C ++では、継承ではなく概念を使用してこれを実装する可能性が高くなります。「ジェネリックプログラミング」のアイデアを検討する必要があります。ブーストのウェブサイトにはまともな紹介があります:http ://www.boost.org/community/generic_programming.html

于 2012-05-25T18:32:11.247 に答える