1

C# では、カスタム列挙を非常に簡単に定義できます。たとえば、次のようになります。

public IEnumerable<Foo> GetNestedFoos()
{
    foreach (var child in _SomeCollection)
    {
        foreach (var foo in child.FooCollection)
        {
            yield return foo;
        }
        foreach (var bar in child.BarCollection)
        {
            foreach (var foo in bar.MoreFoos)
            {
                yield return foo;
            }
        }
    }
    foreach (var baz in _SomeOtherCollection)
    {
        foreach (var foo in baz.GetNestedFoos())
        {
            yield return foo;
        }
    }
}

(これは、LINQ とより優れたカプセル化を使用して簡略化できますが、それは問題のポイントではありません。)


C++11 では、同様の列挙を行うことができますが、代わりにビジター パターンが必要です。

template<typename Action>
void VisitAllFoos(const Action& action)
{
    for (auto& child : m_SomeCollection)
    {
        for (auto& foo : child.FooCollection)
        {
            action(foo);
        }
        for (auto& bar : child.BarCollection)
        {
            for (auto& foo : bar.MoreFoos)
            {
                action(foo);
            }
        }
    }
    for (auto& baz : m_SomeOtherCollection)
    {
        baz.VisitAllFoos(action);
    }
}

内部でビジターを呼び出すのではなく、関数が外部で反復できる範囲を返す最初のようなことを行う方法はありますか?

(そして、 a を作成しstd::vector<Foo>て返すという意味ではありません。インプレース列挙である必要があります。)

Boost.Range ライブラリがソリューションに関与していると思われることは知っていますが、特に詳しくはありません。

この種のことを行うためにカスタムイテレータを定義することが可能であることも認識しています(これも答えに関係していると思われます)が、書きやすいもの、理想的には示されている例よりも複雑ではないものを探していますここにあり、構成可能です(のように_SomeOtherCollection)。

必要に応じて内部でラムダを使用することは気にしませんが (ただし、ラムダを使用することは避けたいと思います)。

4

4 に答える 4

0

boost::asio::coroutineこれは;の助けを借りて行うことができます。https://pubby8.wordpress.com/2014/03/16/multi-step-iterators-using-coroutines/およびhttp://www.boost.org/doc/libs/1_55_0/doc/html/で例を参照してください。 boost_asio/overview/core/coroutine.html .

于 2015-07-05T23:50:48.877 に答える
0

できることは、独自のアダプター関数を作成し、同じ型のさまざまな範囲の要素で呼び出すことです。

これはテストされていないソリューションであり、コンパイルするにはおそらく微調整が必​​要ですが、アイデアが得られます。可変個引数テンプレートを使用して、コレクションから次のコレクションに移動します。

template<typename Iterator, Args...>
visitAllFoos(std::pair<Iterator, Iterator> collection, Args&&... args)
{
  std::for_each(collection.first, collection.second, {}(){ // apply action });
  return visitAllFoos(std::forward<Args>(args)...);
}

//you can call it with a sequence of begin/end iterators
visitAllFoos(std::make_pair(c1.begin(), c1,end()), std::make_pair(c2.begin(), c2,end()))
于 2015-07-03T09:47:32.937 に答える
0

あなたがやろうとしていることは、Boost.Range、特にjoinand any_rangeで実行できると思います(後者は、コンテナーのタイプを非表示にしてインターフェイスから削除する場合に必要になりjoined_rangeます)。

ただし、結果として得られるソリューションは、複雑さとパフォーマンスの両方の点であまり実用的ではありません。主に、ネストされたjoined_rangeと によって発生する型消去のオーバーヘッドが原因ですany_range。個人的には、std::vector<Foo*>訪問を構築または使用するだけです。

于 2015-07-03T21:36:00.580 に答える
0

あなたの質問を正しく理解していれば、コレクションのすべての要素に対して何らかのアクションを実行したいと考えています。

C++ には、 iterator ヘッダーで定義された広範な一連の反復子操作があります。std::vector参照する を含むほとんどのコレクション構造には、引数を取らず、構造の先頭と末尾にイテレータを返すメソッドがあります.begin.endこれらの反復子には、手動で実行できるいくつかの操作がありますが、主な用途は、いくつかの非常に便利な反復関数を定義するアルゴリズム ヘッダーの形式です。

あなたの特定のケースでfor_eachは、(最初​​から最後までのイテレータとして)範囲と関数を適用する関数が必要だと思います。したがって、関数 (または関数オブジェクト) が呼び出されaction、それを というベクトルに適用したい場合data、次のコードは正しいでしょう (必要なすべてのヘッダーが適切に含まれていると仮定します)。

std::for_each(data.begin(), data.end(), action);

for_eachは、アルゴリズム ヘッダーによって提供される多くの関数の 1 つにすぎないことに注意してください。また、コレクションの検索、一連のデータのコピー、リストの並べ替え、最小値/最大値の検索などの関数も提供します。これらはすべて、反復子を持つ任意の構造で機能するように一般化されています。これらでも十分でない場合は、イテレータでサポートされている操作を読んで独自のものを作成できます。単純に、さまざまなタイプの反復子を受け取るテンプレート関数を定義し、必要な反復子の種類を文書化します。

template <typename BidirectionalIterator>
void function(BidirectionalIterator begin, BidirectionalIterator end) {
    // Do something
}

最後に、これまでに説明したすべての操作は、サイズがわかっている場合は配列に対しても正しく機能するということです。.beginand と書く代わりに、andと.end書きます。ここで、 は配列のサイズです。配列の型をポインターに減衰させて有効なイテレーターにするために、単純なゼロの追加が必要になることがよくありますが、配列ポインターは実際には、他のコンテナー イテレーターと同様にランダム アクセス イテレーターです。+ 0+ nn

于 2015-07-03T03:36:49.387 に答える