⛓️

[C++] <ranges>のviewを見る9 - take_while_view

2020/10/20に公開

take_while_view

take_while_viewは元となるシーケンスから指定された条件を満たす連続要素によるシーケンスを生成するViewです。

#include <ranges>

int main() {
  // 先頭から7未満の要素だけを取り出す
  std::ranges::take_while_view tv{std::views::iota(1), [](int n) { return n < 7; }};
  
  for (int n : tv) {
    std::cout << n; // 123456
  }
}

元となるシーケンスの先頭から条件を満たさない最初の要素の一つ前までのシーケンスを生成します。

take_while_viewは元となるrangeの性質をそのまま受け継ぎ(というかイテレータをそのまま利用し)、元となるrangeと同じカテゴリになります。

遅延評価

take_while_viewもまた、遅延評価によってシーケンスを生成します。ただ、take_while_viewは元となるrangeの極々薄いラッパーなので、ほとんどの操作はベースにあるイテレータの操作をそのまま呼び出すだけで、特別な事は行ないません。

take_while_viewが行なっている事は終端の管理だけで、==による終端チェックのタイミングで現在の要素が条件を満たすか否かをチェックします。また、同時に現在の位置が元のシーケンスの終端に到達しているかもチェックすることでオーバーランを防止します。

// take_while_view構築時には何もしない
std::ranges::take_while_view tv{std::views::iota(1), [](int n) { return n < 7; }};

// イテレータ取得時には元のイテレータをそのまま返す
auto it = std::ranges::begin(tv);

// インクリメントは元のイテレータのインクリメント
++it;

// 間接参照も元のイテレータの間接参照
int n1 = *it; // n1 == 2

// 受け取った述語オブジェクトを私て番兵を構築する
auto fin = std::ranges::end(tv);

// 終端チェック時に現在のイテレータの指す要素が与えられた条件を満たしているかをチェック
// 同時に元のシーケンスの終端に到達しているかもチェックする
it == fin;

この特性上、==による終端チェック時には毎回要素の参照と条件のチェックが行われます。条件判定にはあまり重い処理を渡さないように気を付けましょう。

views::take_while

take_while_viewに対応するrange adaptor objectstd::views::take_whileです。

#include <ranges>

int main() {
  for (int n : std::views::take_while(std::views::iota(1), [](int n) { return n < 7; })) {
    std::cout << n; // 123456
  }
  
  std::cout << '\n';

  // パイプラインスタイル
  for (int n : std::views::iota(1) | std::views::take_while([](int n) { return n < 7; })) {
    std::cout << n; // 123456
  }
}

views::take_whileはカスタマイゼーションポイントオブジェクトであり、2つの引数を受け取りそれらを転送してtake_while_viewを構築して返します。

Discussion