両方を使用して、ある範囲の要素に関数を適用できます。
高レベルで:
std::for_each
は、関数の戻り値を無視し、実行の順序を保証します。std::transform
は、戻り値を反復子に割り当てますが、実行順序を保証しません。どちらを使用するのが望ましいのですか?微妙な注意事項はありますか?
std::transform
はmap
と同じです。このアイデアは、2つのイテレータの間にある各要素に関数を適用し、そのような関数の適用から生じる要素で構成される別のコンテナを取得することです。たとえば、オブジェクトのデータメンバーを新しいコンテナに投影するために使用できます。以下では、std::transform
を使用して、std::string
sのコンテナー内のstd::size_t
sのコンテナーを変換します。
std::vector<std::string> names = {"hi", "test", "foo"};
std::vector<std::size_t> name_sizes;
std::transform(names.begin(), names.end(), std::back_inserter(name_sizes), [](const std::string& name) { return name.size();});
一方、唯一の副作用のためにstd::for_each
を実行します。つまり、std::for_each
は、範囲ベースの単純なfor
ループに非常に似ています。
文字列の例に戻ります。
std::for_each(name_sizes.begin(), name_sizes.end(), [](std::size_t name_size) {
std::cout << name_size << std::endl;
});
実際、C++ 11以降では、範囲ベースのfor
ループを使用した簡潔な表記法で同じことが実現できます。
for (std::size_t name_size: name_sizes) {
std::cout << name_size << std::endl;
}
高レベルの概要
- _
std::for_each
_は、関数の戻り値を無視し、実行の順序を保証します。- _
std::transform
_は戻り値を反復子に割り当てますが、実行の順序を保証しません。
ほぼカバーしています。
別の見方(どちらかを優先する);
留意すべきもう1つの点(微妙な注意点)は、C +の前後の _std::transform
_ の操作の要件の変更です。 +11(en.cppreference.comから);
基本的に、これらは未決定の実行順序を許可することでした。
一方を他方の上で使用するのはいつですか?
範囲内の各要素を操作する場合は、_for_each
_を使用します。各要素から何かを計算する必要がある場合は、transform
を使用します。 _for_each
_およびtransform
を使用する場合、通常はそれらをラムダとペアにします。
そうは言っても、C++ 11での範囲ベースのfor
ループとラムダ(for (element : range)
)の出現以来、従来の_for_each
_の現在の使用はやや減少していることがわかります。その構文と実装は非常に自然であり(ただし、ここでの距離は異なります)、いくつかのユースケースにもっと直感的にフィットします。
質問は回答されましたが、この例によって違いがさらに明確になると思います。
for_each
は非変更STL操作に属します。つまり、これらの操作はコレクションの要素またはコレクション自体を変更しません。したがって、for_eachによって返される値は常に無視され、コレクション要素に割り当てられません。それでも、たとえば参照を使用して要素がf関数に渡される場合など、コレクションの要素を変更することは可能です。 STLの原則と一致しないため、このような動作は避けてください。
対照的に、transform
関数はSTL操作の変更に属し、特定の述語(unary_opまたはbinary_op)をコレクションの要素に適用し、結果を別のコレクションに格納します。
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
void printer(int i) {
cout << i << ", ";
}
int main() {
int mynumbers[] = { 1, 2, 3, 4 };
vector<int> v(mynumbers, mynumbers + 4);
for_each(v.begin(), v.end(), negate<int>());//no effect as returned value of UnaryFunction negate() is ignored.
for_each(v.begin(), v.end(), printer); //guarantees order
cout << endl;
transform(v.begin(), v.end(), v.begin(), negate<int>());//negates elements correctly
for_each(v.begin(), v.end(), printer);
return 0;
}
印刷されます:
1, 2, 3, 4,
-1, -2, -3, -4,
Std :: tranformの実際の使用例は、文字列を大文字に変換する場合です。次のようなコードを記述できます。
std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);
次のようなstd :: for_eachで同じことを達成しようとする場合:
std::for_each(s.begin(), s.end(), ::toupper);
大文字の文字列に変換しません