- ranges[meta header]
- std::ranges[meta namespace]
- function template[meta id-type]
- cpp23[meta cpp]
namespace std::ranges {
template<class C, input_range R, class... Args> requires (!view<C>)
constexpr C to(R&& r, Args&&... args); // (1)
template<template<class...> class C, input_range R, class... Args>
constexpr auto to(R&& r, Args&&... args); // (2)
template<class C, class... Args> requires (!view<C>)
constexpr auto to(Args&&... args); // (3)
template<template<class...> class C, class... Args>
constexpr auto to(Args&&... args); // (4)
}
Rangeの各要素を要素とするコンテナを構築する。 この関数は再帰的に適用され、ネストされたRangeにも対応する。
- (1): 処理の実体となる関数テンプレート
- (2): (1)のテンプレートテンプレート版
- (3): (1)に対応するRangeアダプタ
- (4): (2)に対応するRangeアダプタ
(2), (4) の存在により、コンテナ型を指定するときにその要素の型を省略して(=テンプレートとして)渡すことができる。
to
は他のRangeアダプタとは異なり先行評価される。コンテナもRangeなのでto
をRangeパイプラインの中間に組み込むこともできるが、view
だけで構成する場合とは振る舞いが異なることに注意。
- (1), (3):
C
はCV修飾のないクラス型であること。
(1) Rangeの各要素を要素とするコンテナCのオブジェクトを以下の通りに構築して返す。
C
がinput_range
ではないか、convertible_to
<
range_reference_t
<R>,
range_value_t
<C>>
である(R
の要素への参照がC
の要素に変換できる)場合:
constructible_from
<C, R, Args...>
である(C
がR
と残りの引数で構築できる)場合C(
std::forward
<R>(r),
std::forward
<Args>(args)...)
constructible_from
<C,
from_range_t
, R, Args...>
である(C
がfrom_range_t
、R
、残りの引数で構築できる)場合C(
from_range
,
std::forward
<R>(r),
std::forward
<Args>(args)...)
common_range
<R>
がtrue
で、iterator_traits
<
iterator_t
<R>>::iterator_category
がinput_iterator_tag
から派生する有効な型であり、constructible_from
<C,
iterator_t
<R>,
sentinel_t
<R>, Args...>
である(C
がR
のイテレータおよび番兵と残りの引数で構築できる)場合C(
ranges::begin(r)
,
ranges::end(r)
,
std::forward
<Args>(args)...)
constructible_from
<C, Args...>
がtrue
で、container-insertable<C,
range_reference_t
<R>>
がtrue
である場合- 以下のコードで初期化する
C c(std::forward<Args>(args)...);
if constexpr (sized_range<R> && reservable-container<C>) {
c.reserve(static_cast<range_size_t<C>>(ranges::size(r)));
}
ranges::copy(r, container-inserter<range_reference_t<R>>(c));
- ranges::size[link /reference/ranges/size.md]
- ranges::copy[link /reference/algorithm/ranges_copy.md]
input_range
<
range_reference_t
<R>>
である場合:
to<C>(r | views::transform([](auto&& elem) {
return to<range_value_t<C>>(std::forward<decltype(elem)>(elem));
}), std::forward<Args>(args)...);
- views::transform[link transform_view.md]
どの条件にもあてはまらない場合、プログラムは不適格である。
(2) input-iterator
を次のように定義する。
struct input-iterator {
using iterator_category = input_iterator_tag;
using value_type = range_value_t<R>;
using difference_type = ptrdiff_t;
using pointer = add_pointer_t<range_reference_t<R>>;
using reference = range_reference_t<R>;
reference operator*() const;
pointer operator->() const;
input-iterator& operator++();
input-iterator operator++(int);
bool operator==(const input-iterator&) const;
};
- input_iterator_tag[link /reference/iterator/iterator_tag.md]
- ptrdiff_t[link /reference/cstddef/ptrdiff_t.md]
- add_pointer_t[link /reference/type_traits/add_pointer.md]
また、DEDUCE_EXPR
を次のように定義する。
- 有効な式ならば、
C(
declval
<R>(),
declval
<Args>()...)
- 有効な式ならば、
C(
from_range
,
declval
<R>(),
declval
<Args>()...)
- 有効な式ならば、
C(
declval
<input-iterator>(),
declval
<input-iterator>(),
declval
<Args>()...)
- 1-3が有効でなければ、ill-formed
このとき、戻り値は to<
decltype
(DEDUCE_EXPR)>(
std::forward
<R>(r),
std::forward
<Args>(args)...)
(3), (4): 次の性質をもつ完全転送呼び出しラッパー(perfect forwarding call wrapper)であるようなRangeアダプタクロージャオブジェクトf
を返す。
- ターゲットオブジェクトを持たない
- バインドされた引数
bound_args
は、decay_t
<Args>...
型のオブジェクトであり、それぞれstd::forward
<Args>(args)...
で直接非リスト初期化(direct-non-list-initialize)される - 呼び出しパターンは
to<C>(r, bound_args...)
である。ただし、r
はf
の関数呼び出し式で使用される引数
本説明に用いる説明専用要素を以下のように定義する。
// reservable-container: 容量をあらかじめ確保できることを要求するコンセプト
template<class Container>
constexpr bool reservable-container =
sized_range<Container> &&
requires(Container& c, range_size_t<Container> n) {
c.reserve(n); // コンテナのサイズ型を引数とするreserveメンバ関数がある
{ c.capacity() } -> same_as<decltype(n)>; // コンテナのサイズ型を返すcapacityメンバ関数がある
{ c.max_size() } -> same_as<decltype(n)>; // コンテナのサイズ型を返すmax_sizeメンバ関数がある
};
// container-insertable: push_backまたはinsertが使えることを要求するコンセプト
template<class Container, class Ref>
constexpr bool container-insertable =
requires(Container& c, Ref&& ref) {
requires (requires { c.push_back(std::forward<Ref>(ref)); } ||
requires { c.insert(c.end(), std::forward<Ref>(ref)); });
};
// container-inserter: push_backが使えればback_inserter, そうでなければinserterを返す関数
template<class Ref, class Container>
constexpr auto container-inserter(Container& c) {
if constexpr (requires { c.push_back(declval<Ref>()); }) {
return back_inserter(c);
} else {
return inserter(c, c.end());
}
}
- back_inserter[link /reference/iterator/back_inserter.md]
- inserter[link /reference/iterator/inserter.md]
Rangeの各要素を要素とするコンテナを構築するには、従来、次のような方法があった。
#include <ranges>
#include <vector>
#include <string>
#include <string_view>
#include <print>
#include <algorithm>
int main() {
using namespace std;
using namespace std::literals;
auto str = "the quick brown fox"sv;
vector<string> words;
for(auto&& i : views::split(str, ' ')) {
words.emplace_back(i.begin(), i.end());
}
println("{}", words);
}
- views::split[link split_view.md]
- vector[link /reference/vector/vector.md]
- string[link /reference/string/basic_string.md]
これを簡潔に書くために、to
関数が提案された。
#include <ranges>
#include <vector>
#include <string>
#include <string_view>
#include <print>
int main() {
using namespace std;
using namespace std::literals;
auto str = "the quick brown fox"sv;
auto words = views::split(str, ' ') | ranges::to<vector<string>>();
println("{}", words);
}
- ranges::to[color ff0000]
- views::split[link split_view.md]
- vector[link /reference/vector/vector.md]
- string[link /reference/string/basic_string.md]
["the", "quick", "brown", "fox"]
- C++23
- Clang: 19.0 (at least)
- GCC: ??
- ICC: ??
- Visual C++: ??