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++23:
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)); -
C++26
C c(std::forward<Args>(args)...); if constexpr (approximately_sized_range<R> && reservable-container<C>) { c.reserve(static_cast<range_size_t<C>>(ranges::reserve_hint(r))); } ranges::copy(r, container-inserter<range_reference_t<R>>(c)); -
-
input_range<range_reference_t<R>>である場合:
to<C>(ref_view(r) | views::transform([](auto&& elem) {
return to<range_value_t<C>>(std::forward<decltype(elem)>(elem));
}), std::forward<Args>(args)...);
どの条件にもあてはまらない場合、プログラムは不適格である。
(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;
};
また、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: emplace_back/push_back/emplace/insertのいずれかが使えることを要求するコンセプト
template<class Container, class Ref>
constexpr bool container-insertable =
requires(Container& c, Ref&& ref) {
requires (requires { c.emplace_back(declval<Ref>()); } ||
requires { c.push_back(declval<Ref>()); } ||
requires { c.emplace(c.end(), declval<Ref>()); } ||
requires { c.insert(c.end(), declval<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());
}
}
この機能が必要になった背景・経緯
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);
}
これを簡潔に書くために、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);
}
出力
["the", "quick", "brown", "fox"]
バージョン
言語
- C++23
処理系
- Clang: 19.0 (at least) ✅
- GCC: ??
- ICC: ??
- Visual C++: ??
参照
- 26.5.7.2 ranges::to
- P2846R6
reserve_hint: Eagerly reserving memory for not-quite-sized lazy ranges- C++26で要素数の事前確保に
ranges::sizeの代わりにranges::reserve_hintを使用するよう変更
- C++26で要素数の事前確保に
- LWG Issue 3733.
ranges::tomisuses cpp17-input-iterator- C++23で、Cpp17InputIterator判定に説明専用コンセプト
cpp17-input-iteratorを使用するのをやめ、iterator_traits<iterator_t<R>>::iterator_categoryがinput_iterator_tagから派生する有効な型であることを判定するよう修正された(common_iteratorのようにiterator_traitsの特殊化を持つイテレータで正しく判定されるようにするため)
- C++23で、Cpp17InputIterator判定に説明専用コンセプト
- LWG Issue 3743.
ranges::to'sreservemay be ill-formed- C++23で、
reserveに渡す値をstatic_cast<range_size_t<C>>(ranges::size(r))と明示キャストするよう修正され、整数クラス型を返すranges::sizeが暗黙変換できず不適格になる問題が解消された
- C++23で、
- LWG Issue 3785.
ranges::tois over-constrained on the destination type being a range- C++23で、変換先
Cがinput_rangeでない場合の分岐条件が加えられ、range_value_t<C>が不適格になる問題が回避された
- C++23で、変換先
- LWG Issue 3847.
ranges::tocan still return views- C++23で、(1)(3)に
Cがcv非修飾のクラス型であることの適格要件が追加され、viewが返される問題が解消された
- C++23で、(1)(3)に
- LWG Issue 3984.
ranges::to's recursion branch may be ill-formed- C++26で、再帰分岐において
rをranges::ref_viewで包むよう修正され、rがviewでない左辺値rangeの場合に不適格となる問題が解消された
- C++26で、再帰分岐において
- LWG Issue 4016. container-insertable checks do not match what container-inserter does
- C++26で、説明専用コンセプト
container-insertableの判定がemplace_back/push_back/emplace/insertの4つを試す形に修正され、実際の挿入処理と一致するようになった
- C++26で、説明専用コンセプト