132

C ++ 11は、コンテナーを反復処理するための複数の方法を提供します。例えば:

範囲ベースのループ

for(auto c : container) fun(c)

std :: for_each

for_each(container.begin(),container.end(),fun)

ただし、次のようなことを実現するために、同じサイズの2つ(またはそれ以上)のコンテナーを反復処理するための推奨される方法は何ですか。

for(unsigned i = 0; i < containerA.size(); ++i) {
  containerA[i] = containerB[i];
}
4

10 に答える 10

40

あなたの特定の例では、単に使用してください

std::copy_n(contB.begin(), contA.size(), contA.begin())

より一般的なケースでは、Boost.Iterator をzip_iterator使用して、範囲ベースの for ループで使用できるようにする小さな関数を使用できます。ほとんどの場合、これでうまくいきます:

template<class... Conts>
auto zip_range(Conts&... conts)
  -> decltype(boost::make_iterator_range(
  boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
  boost::make_zip_iterator(boost::make_tuple(conts.end()...))))
{
  return {boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
          boost::make_zip_iterator(boost::make_tuple(conts.end()...))};
}

// ...
for(auto&& t : zip_range(contA, contB))
  std::cout << t.get<0>() << " : " << t.get<1>() << "\n";

実例。

ただし、本格的な汎用性のためには、おそらくthisbegin()のようなものが必要になるでしょう。これは、メンバー/end()持たないが名前空間にbegin/関数を持つ配列およびユーザー定義型に対して正しく機能します。endまた、これにより、ユーザーは機能constを介して具体的にアクセスできるようになりzip_c...ます。

もしあなたが私のように素敵なエラーメッセージの支持者なら、おそらくthiszip_...が欲しいでしょう。これは、関数のいずれかに一時コンテナが渡されたかどうかをチェックし、そうであれば素敵なエラーメッセージを出力します。

于 2012-09-23T15:04:59.103 に答える
9

ヘッダーで提供されているように、複数のコンテナーで特定のことを行う方法はたくさんあります。algorithmたとえば、あなたが与えた例でstd::copyは、明示的なforループの代わりに使用することができます。

一方、通常のforループ以外に、複数のコンテナーを一般的に反復する組み込みの方法はありません。反復する方法はたくさんあるので、これは驚くべきことではありません。考えてみてください。1つのコンテナを1つのステップで、1つのコンテナを別のステップで繰り返すことができます。または、一方のコンテナを最後まで通過してから、もう一方のコンテナの最後まで挿入を開始します。または、他のコンテナを完全に通過して最初からやり直すたびに、最初のコンテナの1つのステップ。または他のパターン。または一度に2つ以上のコンテナ。など..。

ただし、最短のコンテナの長さまで2つのコンテナを反復処理する独自の「for_each」スタイルの関数を作成する場合は、次のようにすることができます。

template <typename Container1, typename Container2>
void custom_for_each(
  Container1 &c1,
  Container2 &c2,
  std::function<void(Container1::iterator &it1, Container2::iterator &it2)> f)
  {
  Container1::iterator begin1 = c1.begin();
  Container2::iterator begin2 = c2.begin();
  Container1::iterator end1 = c1.end();
  Container2::iterator end2 = c2.end();
  Container1::iterator i1;
  Container2::iterator i2;
  for (i1 = begin1, i2 = begin2; (i1 != end1) && (i2 != end2); ++it1, ++i2) {
    f(i1, i2);
  }
}

もちろん、同様の方法で、任意の種類の反復戦略を作成できます。

もちろん、内部のforループを直接実行する方が、このようなカスタム関数を作成するよりも簡単であると主張するかもしれません...そして、1回か2回しか実行しないのであれば、あなたは正しいでしょう。しかし、良い点は、これが非常に再利用可能であることです。=)

于 2012-09-23T14:40:41.710 に答える
6

別の解決策は、ラムダで他のコンテナのイテレータの参照をキャプチャし、その上でポストインクリメント演算子を使用することです。たとえば、単純なコピーは次のようになります。

vector<double> a{1, 2, 3};
vector<double> b(3);

auto ita = a.begin();
for_each(b.begin(), b.end(), [&ita](auto &itb) { itb = *ita++; })

ラムダ内では、何でも実行しitaてからインクリメントできます。これは、複数のコンテナのケースに簡単に拡張できます。

于 2016-06-09T20:15:04.963 に答える
2

<algorithm>個人的には、可能であればSTL (ヘッダー) に既に含まれているものを使用することを好みます。std::transform2 つの入力反復子を取ることができる署名があります。したがって、少なくとも 2 つの入力コンテナーの場合は、次のようにすることができます。

std::transform(containerA.begin(), containerA.end(), containerB.begin(), outputContainer.begin(), [&](const auto& first, const auto& second){
    return do_operation(first, second);
});

outputContainerが入力コンテナの 1 つになることもあることに注意してください。ただし、1 つの制限は、配置されているコンテナーの 1 つを変更している場合、更新後の操作を実行できないことです。

于 2020-09-23T21:28:56.553 に答える