web-dev-qa-db-ja.com

C#のIEnumerable <T>に相当する標準C ++はありますか?

または、Tの列挙子が単にすべての要素をリストしている場合は、ベクトルを使用しても安全ですか?

38
derekhh

C++では必要ありません。その理由は次のとおりです。

C#は動的ポリモーフィズムのみをサポートします。したがって、再利用可能なアルゴリズムを作成するには、すべての反復子が実装するインターフェースが必要です。それが_IEnumerator<T>_であり、_IEnumerable<T>_はイテレータを返すためのファクトリです。

一方、C++テンプレートはダックタイピングをサポートしています。つまり、メンバーにアクセスするためにインターフェイスでジェネリック型パラメーターを制約する必要はありません。コンパイラーは、テンプレートの個々のインスタンス化ごとにメンバーを名前で検索します。

C++コンテナーとイテレーターには、.NET _IEnumerable<T>_、_IEnumerator<T>_、_ICollection<T>_、_IList<T>_と同等の暗黙的なインターフェースがあります。

コンテナの場合:

  • iteratorおよび_const_iterator_ typedefs
  • begin()メンバー関数-IEnumerable<T>::GetEnumerator()の必要を満たす
  • end()メンバー関数-IEnumerator<T>::MoveNext()の代わりに戻り値

順方向反復子の場合:

  • _value_type_ typedef
  • _operator++_-IEnumerator<T>::MoveNext()の代わり
  • _operator*_および_operator->_-_IEnumerator<T>::Current_の代わり
  • _operator*_インデクサーセッターの代わりに_IList<T>_からの戻り値の型を参照
  • _operator==_および_operator!=_-.NETでは真の同等物はありませんが、コンテナのend()IEnumerator<T>::MoveNext()の戻り値と一致します

ランダムアクセス反復子の場合:

  • _operator+_、_operator-_、_operator[]_-_IList<T>_の代わり

これらを定義すると、標準アルゴリズムがコンテナとイテレータで機能します。インターフェイスは必要ありません、仮想機能は必要ありません。仮想関数を使用しないと、C++の汎用コードが同等の.NETコードよりも高速になり、場合によってははるかに高速になります。


注:一般的なアルゴリズムを作成するときは、コンテナーメンバー関数の代わりにstd::begin(container)およびstd::end(container)を使用するのが最適です。これにより、STLコンテナーに加えて、生の配列(メンバー関数を持たない)でアルゴリズムを使用できます。生の配列と生のポインタは、この1つの例外を除いて、コンテナとイテレータの他のすべての要件を満たします。

48
Ben Voigt

標準のC++の方法は、2つの反復子を渡すことです。

template<typename ForwardIterator>
void some_function(ForwardIterator begin, ForwardIterator end)
{
    for (; begin != end; ++begin)
    {
        do_something_with(*begin);
    }
}

クライアントコードの例:

std::vector<int> vec = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(vec.begin(), vec.end());

std::list<int> lst = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(lst.begin(), lst.end());

int arr[] = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(arr + 0, arr + 8);

やったジェネリックプログラミング!

8
fredoverflow

私たちが質問に厳密に固執する場合、私の知る限り、答えはノーです。人々はC++で利用できる代替物は何であるかについて返答し続けました。これは良い情報ではあるかもしれませんが、回答ではなく、OPがおそらくすでに知っていました。

「必要ない」というのはまったく同意できません。C++と.NETの標準ライブラリのデザインが異なるだけです。 IEnumerable <>の主な機能は多態性であるため、呼び出し元が必要なクラス(配列、リスト、セットなど)を使用しながら、ライブラリAPIでもコンパイル時の強力な型指定、フェイルセーフを提供できます。 。

C++での唯一の代替手段はテンプレートです。しかし、C++テンプレートは安全に型指定されたランタイムジェネリックではなく、基本的にマクロの一種です。したがって、まずC++のテンプレートを使用すると、テンプレートを使用する必要のあるユーザーにテンプレートのソースコード全体を提供する必要があります。さらに、ライブラリAPIをテンプレート化すると、その呼び出しがコンパイルされることを保証できなくなり、コードは自動的に自己文書化されません。

私は、C#とC++の両方を使用していて、この点に不満を持っている他のプログラマーに完全に同情しています。

ただし、C++ 2Xは範囲(OPを満たす可能性があるか?)を含む機能を追加する予定です。また、概念(テンプレートの弱い/悪い型チェックに対処する-欠陥 Bjarne Stroustrupによって承認された 自身)、およびモジュール(ヘッダーのみのテンプレートの痛みを軽減するのに役立つ場合とそうでない場合がある) )。

5
J.P.

IEnumerable<T>vectorと概念的に非常に異なります。

IEnumerableforward-onlyread-onlyオブジェクトを保持するコンテナ(存在する場合)に関係なく、オブジェクトのシーケンスへのアクセス。 vectorは実際にはコンテナそのものです。

C++では、このコンテナーの詳細を指定せずにコンテナーへのアクセスを提供する場合、規則は、コンテナーの開始と終了を表す2つの反復子を渡すことです。

良い例が accumulate のC++ STL定義です。これは IEnumerable <T> .Aggregate と対照的です。

C++では

   int GetProduct(const vector<int>& v)
   {
         // We don't provide the container, but two iterators
         return std::accumulate(v.begin(), v.end(), 1, multiplies<int>());
   }

C#で

  int GetProduct(IEnumerable<int> v)
  {
        v.Aggregate(1, (l, r) => l*r);
  }
4
Andrew Shepherd