web-dev-qa-db-ja.com

すべての<algorithm>関数がコンテナーではなく範囲のみを使用するのはなぜですか?

_<algorithm>_には多くの便利な関数がありますが、それらはすべて「シーケンス」、つまりイテレータのペアで動作します。たとえば、コンテナがあり、_std::accumulate_を実行したい場合は、次のように記述する必要があります。

_std::vector<int> myContainer = ...;
int sum = std::accumulate(myContainer.begin(), myContainer.end(), 0);
_

私がやろうとしているのは、

_int sum = std::accumulate(myContainer, 0);
_

私の目には、これはもう少し読みやすく、明確です。

コンテナの一部のみを操作したい場合があるかもしれないので、optionを渡して範囲を指定すると便利です。しかし、少なくとも私の経験では、それはまれな特別なケースです。私は通常、コンテナ全体を操作したいと思います。

コンテナーを取り、その上でbegin()end()を呼び出すラッパー関数を書くのは簡単ですが、そのような便利な関数は標準ライブラリには含まれていません。

このSTL設計の選択の背後にある理由を知りたいのですが。

52
lethal-guitar

...範囲を渡すオプションがあると間違いなく便利です。しかし、少なくとも私の経験では、それはまれな特別なケースです。通常はコンテナ全体を操作したい

あなたの経験ではまれな特別なケースかもしれませんが、実際には全コンテナは特殊なケースであり、任意の範囲が一般的なケースです。

whole containerケースを現在のインターフェースを使用して実装できることはすでに気づいていますが、その逆はできません。

したがって、ライブラリ作成者は、2つのインターフェースを事前に実装するか、すべてのケースをカバーする1つだけを実装するかを選択できました。


コンテナーを取り、コンテナーのbegin()およびend()を呼び出すラッパー関数を書くのは簡単ですが、そのような便利な関数は標準ライブラリに含まれていません

True、特に無料の関数std::beginおよびstd::endが含まれるようになりました。

したがって、ライブラリが便利なオーバーロードを提供するとします:

template <typename Container>
void sort(Container &c) {
  sort(begin(c), end(c));
}

現在は、比較ファンクターを使用して同等のオーバーロードを提供する必要があり、他のすべてのアルゴリズムにも同等のオーバーロードを提供する必要があります。

しかし、少なくともフルコンテナで操作したい場合はすべてカバーしました。まあ、かなり。検討する

std::for_each(c.rbegin(), c.rend(), foo);

コンテナでbackwardsを処理する場合は、anotherメソッドが必要です(またはメソッドのペア)既存のアルゴリズムごと。


したがって、範囲ベースのアプローチは、以下の単純な意味でより一般的です。

  • コンテナ全体のバージョンで可能なすべてのことを実行できます
  • コンテナー全体のアプローチでは、必要なオーバーロードの数が2倍または3倍になりますが、依然として強力ではありません
  • 範囲ベースのアルゴリズムも構成可能です(イテレーターアダプターをスタックまたはチェーンできますが、これは関数型言語とPythonでより一般的に行われます)。

もちろん、もう1つの正当な理由があります。それは、STLを標準化するためにすでに多くの作業があり、その前にコンビニエンスラッパーでそれを膨らませていたことです。広く使用されていたため、委員会の限られた時間を有効に活用することはできません。興味がある場合は、Stepanov&Leeのテクニカルレポートをご覧ください here

コメントで述べたように、 Boost.Range は、標準を変更せずに新しいアプローチを提供します。

41
Useless

この件に関して Herb Sutterによる記事 があることがわかりました。基本的に、問題はオーバーロードのあいまいさです。次の場合:

template<typename Iter>
void sort( Iter, Iter ); // 1

template<typename Iter, typename Pred>
void sort( Iter, Iter, Pred ); // 2

そして、以下を追加します:

template<typename Container>
void sort( Container& ); // 3

template<typename Container, typename Pred>
void sort( Container&, Pred ); // 4

41を適切に区別することが難しくなります。

提案されているが、最終的にはC++ 0xに含まれていない概念はそれを解決し、enable_ifを使用してそれを回避することもできます。一部のアルゴリズムでは、まったく問題ありません。しかし、彼らはそれに反対しました。

ここですべてのコメントと回答を読んだ後、rangeオブジェクトが最善の解決策になると思います。 Boost.Rangeを見てみましょう。

21
lethal-guitar

基本的には従来の決定です。イテレーターの概念はポインターでモデル化されますが、コンテナーは配列でモデル化されません。さらに、配列は渡すのが難しいため(一般的に、長さには型ではないテンプレートパラメーターが必要です)、関数で使用できるのはポインターのみです。

しかし、そうです、後から考えると、決定は間違っています。 begin/endまたはbegin/lengthのいずれかから構築可能な範囲オブジェクトを使用したほうがよいでしょう。現在では、代わりに複数の_nサフィックス付きアルゴリズムがあります。

12
MSalters

それらを追加してもパワーは得られず(.begin().end()を自分で呼び出すことでコンテナー全体を既に実行できます)、ライブラリに適切に追加する必要があるものが1つ追加されます。指定、ベンダーによるライブラリーへの追加、テスト、保守など.

要するに、コンテナー全体のユーザーが1つの追加の関数呼び出しパラメーターを入力するのを防ぐためだけに追加のテンプレートのセットを維持するのは面倒なことではないので、おそらくそこにはありません。

5
Michael Kohne