⛓️
[C++] <ranges>のviewを見る6 - filter_view
filter_view
filter_view
は受け取った述語に従って元となるrangeの要素を選別したシーケンスを生成するViewです。
#include <ranges>
int main() {
// 奇数をフィルターする(偶数のみ取り出す)
std::ranges::filter_view fv{std::views::iota(1, 10), [](int n) { return n % 2 == 0; }};
for (int n : fv) {
std::cout << n; // 2468
}
}
名前の通りに、元となるシーケンスの要素をフィルターしたシーケンスを得るものです。
filter_view
のrangeカテゴリは、元となっているrangeのカテゴリによってbidirectional rangeからinput rangeまで変化します。ただ、一番強くてもbidirectional rangeになります。
入力となるシーケンスもフィルタ条件も任意であり結果もまたシーケンスとなるので、filter_view
は色々なところで活躍できそうです。
遅延評価
range adopter全てがそうなのですが、filter_view
によるシーケンス生成は遅延評価されます。イテレータ取得時に最初の要素が計算され、残りの要素はイテレータのインクリメントのタイミングで計算されます。
std::ranges::filter_view fv{std::views::iota(1, 10), [](int n) { return n % 2 == 0; }};
// イテレータ取得時に条件を満たす最初の要素が探索され、`filter_view`の1番目の要素が計算される
// そのイテレータはキャッシュされる
auto it = std::ranges::begin(fv);
// 次に条件を満たす要素が探索され、`filter_view`の2番目の要素が計算される
++it;
int n = *it; // 2番目の要素(4)が得られる
この例ではiota_view
もまた遅延評価されているので、一連のシーケンス全体が遅延評価によって生成されています。
この様に、filter_view
に限らずrange adaptorによる処理チェーンは多くの場合可能な限り遅延評価されます。
views::filter
filter_view
に対応するrange adaptor objectがstd::views::filter
です。
#include <ranges>
int main() {
for (int n : std::views::filter(std::views::iota(1, 10), [](int n) { return n % 2 == 0; })) {
std::cout << n;
}
std::cout << '\n';
// パイプラインスタイル
for (int n : std::views::iota(1, 10) | std::views::filter([](int n) { return n % 2 == 0; })) {
std::cout << n;
}
}
views::filter
もカスタマイゼーションポインオブジェクトであり、rangeと述語オブジェクトを受け取りそれをそのまま転送してfilter_view
を構築して返します。関数呼び出しによって使う場合あまり恩恵はありませんが、パイプラインスタイルで使用するといい感じに書くことができます。
Discussion