⛓️

[C++] <ranges>のviewを見る19 - owning_view

2021/12/03に公開

owning_view

owning_viewは右辺値の範囲を所有して、安全に取り扱えるようにするためのviewです。

#include <ranges>

auto f() -> std::vector<int> {
  return {1, 2, 3, 4, 5};
}

int main() {
  std::ranges::owning_view ov{f()};

  // 安全に使用可能
  for (int n : ov) {
    std::cout << n;  // 12345
  }
}

owning_viewは右辺値のrangeを受け取って、それをムーブして内部に保持することで右辺値範囲の寿命を延長させます。それ以外の部分は受けたrangeのごく薄いラッパーなので、元のrangeの性質をそのまま受け継ぎつつviewとなります。

owning_view<R>rangeであるRの右辺値だけを受け取る事ができて、左辺値から構築する事はできません。また、Rmovableである必要があります。

views::all

owning_viewに対応するrange adaptor objectstd::views::allです。

#include <ranges>

auto f() -> std::vector<int> {
  return {1, 2, 3, 4, 5};
}

int main() {
  auto ov1 = std::views::all(f());
  for (int n : ov1) {
    std::cout << n;  // 12345
  }
  
  std::cout << '\n';

  // パイプラインスタイル(こう書いた時でも安全)
  auto ov2 = f() | std::views::all;
  for (int n : ov2) {
    std::cout << n;  // 12345
  }
}

これはref_viewと共有しています。ref_viewは右辺値範囲を参照できないため、views::allに右辺値範囲が渡された時はowning_viewが得られます。

とはいえ、パイプラインで他のviewを使用する際は、他のviewが入力範囲を自動でviews::allに通してから保持するため、明示的にviews::allを使用する必要はないでしょう(これはref_viewにもいえます)。

viewowning_viewとC++20

このowning_viewref_viewと値カテゴリに関して対になるものですが、当初のC++20には存在していませんでした。

C++20規格(N4861)完成の2020年4月から1年半後の2021年10月、viewの定義を再考する「P2415R2 What is a view?」がC++20に向けて採択されました(?)。これによって、viewの意味論要件が変更され(O(1)で破棄可能という要件が緩和され)、限定的ながら範囲を所有するタイプのviewが実装可能となり、同時にviews::allを右辺値に対して安全にするためにこのowning_viewが導入されました。

C++20規格完成から1年半後にC++20に対して追加されたので、規格書に載っておらず、それをベースとして書かれた解説やリファレンスにも記載が無かったりしますが、owning_viewはC++20のライブラリ機能です。

Discussion