C++20 RangeからC++17互換イテレータペアへの変換にはstd::views::common
レンジアダプタ(range adaptor)を利用する。ただしstd::ranges::basic_istream_view
などムーブのみ/コピー不可なイテレータからなる一部Rangeは変換できない。*1
#include <sstream> #include <ranges> #include <vector> auto ints = std::istringstream{"0 1 2 3 4"}; auto cv = std::ranges::istream_view<int>(ints) | std::views::filter([](int n){ return n % 2 == 0; }) | std::views::common; // NG: ill-formed std::vector<int> vec(cv.begin(), cv.end());
これはstd::ranges::common_view<V>
クラステンプレート制約として、対象Viewのイテレータ型(iterator_t<V>
)がstd::copyable
コンセプトを満たすことを要求するため。C++17互換イテレータではコピー可能・ムーブ可能を要求するが、C++20 Rangeを構成するイテレータはコピー不可・ムーブ可能であればよい。*2
前掲コードのようにRange/Viewからコンテナへ変換したい場合、次期C++2b(C++23)標準ライブラリではstd::views::to
レンジアダプタ(→id:yohhoy:20210902)が利用可能となる。
// C++2b(C++23) #include <sstream> #include <ranges> #include <vector> auto ints = std::istringstream{"0 1 2 3 4"}; auto vec = std::ranges::istream_view<int>(ints) | std::views::filter([](int n){ return n % 2 == 0; }) | std::views::to<std::vector>(); // vec == std::vector<int>{0, 2, 4}
C++20 24.6.4.3, 24.7.13.1, 24.7.13.2より一部引用。
namespace std::ranges { template<movable Val, class CharT, class Traits> requires default_initializable<Val> && stream-extractable<Val, CharT, Traits> class basic_istream_view<Val, CharT, Traits>::iterator { public: // (snip) iterator(const iterator&) = delete; iterator(iterator&&) = default; iterator& operator=(const iterator&) = delete; iterator& operator=(iterator&&) = default; // (snip) }; }
1
common_view
takes a view which has different types for its iterator and sentinel and turns it into aview
of the same elements with an iterator and sentinel of the same type.
2 [Note:common_view
is useful for calling legacy algorithms that expect a range's iterator and sentinel types to be the same. --end note]
3 The nameviews::common
denotes a range adaptor object (24.7.1). (snip)
namespace std::ranges { template<view V> requires (!common_range<V> && copyable<iterator_t<V>>) class common_view : public view_interface<common_view<V>> { // (snip) }; }
関連URL