std::ranges::to
| 在标头 <ranges> 定义
|
||
template< class C, ranges::input_range R, class... Args > requires (!ranges::view<C>) constexpr C to( R&& r, Args&&... args ); |
(1) | (C++23 起) |
template< template< class... > class C, ranges::input_range R, class... Args > constexpr auto to( R&& r, Args&&... args ); |
(2) | (C++23 起) |
template< class C, class... Args > requires (!ranges::view<C>) constexpr /*range adaptor closure*/ to( Args&&... args ); |
(3) | (C++23 起) |
template< template< class... > class C, class... Args > constexpr /*range adaptor closure*/ to( Args&&... args ); |
(4) | (C++23 起) |
| 辅助模板 |
||
template< class Container > constexpr bool /*reservable-container*/ = ranges::sized_range<Container> && requires (Container& c, ranges::range_size_t<Container> n) { c.reserve(n); { c.capacity() } -> std::same_as<decltype(n)>; { c.max_size() } -> std::same_as<decltype(n)>; }; |
(5) | (仅用于阐述*) |
template< class Container, class Reference > constexpr bool /*container-appendable*/ = requires (Container& c, Reference&& ref) { requires ( requires { c.emplace_back(std::forward<Reference>(ref)); } || requires { c.push_back(std::forward<Reference>(ref)); } || requires { c.emplace(c.end(), std::forward<Reference>(ref)); } || requires { c.insert(c.end(), std::forward<Reference>(ref)); } ); }; |
(6) | (仅用于阐述*) |
template< class Reference, class C > constexpr auto /*container-appender*/( C& c ); |
(7) | (仅用于阐述*) |
template< class R, class T > concept /*container-compatible-range*/ = ranges::input_range<R> && std::convertible_to<ranges::range_reference_t<R>, T>; |
(8) | (仅用于阐述*) |
范围转换函数的重载,通过以源范围作为第一个实参来调用接受范围的构造函数、带有 std::from_range_t 标记的范围构造函数、接受迭代器-哨位对的构造函数,或者通过将源范围的每个元素后插入到以实参构造的对象中,来构造新的非视图对象。
r 的元素构造类型为 C 的对象:C 不满足 input_range 或者 std::convertible_to<ranges::range_reference_t<R>, ranges::range_value_t<C>> 为 true:std::constructible_from<C, R, Args...> 为 true,则如同从源范围 std::forward<R>(r) 和其余的函数实参 std::forward<Args>(args)... 进行直接初始化(但不是直接列表初始化)一样,构造非视图对象。std::constructible_from<C, std::from_range_t, R, Args...> 为 true,则如同从额外的消歧标记 std::from_range、源范围 std::forward<R>(r) 和其余的函数实参 std::forward<Args>(args)... 进行直接初始化(但不是直接列表初始化)一样,构造非视图对象。true,则如同从迭代器-哨位对(以 ranges::begin(r) 为迭代器并以 ranges::end(r) 为哨位,其中迭代器和哨位具有相同的类型。换句话说,源范围必须是 common_range)和其余的函数实参 std::forward<Args>(args)... 直接初始化(但不是直接列表初始化)类型为 C 的对象一样,构造非视图对象:
ranges::common_range<R>- 如果
std::iterator_traits<ranges::iterator_t<R>>::iterator_category是有效的,并且代表满足std::derived_fromstd::input_iterator_tag的类型 std::constructible_from<C, ranges::iterator_t<R>, ranges::sentinel_t<R>, Args...>
std::forward<Args>(args)... 直接初始化(但不是直接列表初始化)类型为 C 的对象,并在构造后使用以下等效调用,构造非视图范围对象:
|
|
(C++26 前) |
|
|
(C++26 起) |
如果
R 满足 sized_range(C++26 前)approximately_sized_range(C++26 起) 并且 C 满足 reservable-container,则构造的类型为 C 的对象 c 能够以初始存储大小 ranges::size(r)(C++26 前)ranges::reserve_hint(r)(C++26 起) 预留存储空间,以防止在插入新元素时进行额外的分配。向 c 追加 r 的每个元素。
上述操作在以下两个条件都为 true 时有效:
std::constructible_from<C, Args...>container-appendable<C, ranges::range_reference_t<R>>
to<C>(ranges::ref_view(r) | views::transform([](auto&& elem){return to<ranges::range_value_t<C>>(std::forward<decltype(elem)>(elem));}), std::forward<Args>(args)...)
如果 ranges::input_range<ranges::range_reference_t<C>> 为 true 则允许在范围内嵌套范围构造。
否则,程序非良构。
r 的元素构造推导出的类型的对象。
令 /*input-iterator*/ 为仅用于阐述的满足 老式输入迭代器 (LegacyInputIterator) 的类型:
struct /*input-iterator*/ { using iterator_category = std::input_iterator_tag; using value_type = ranges::range_value_t<R>; using difference_type = std::ptrdiff_t; using pointer = std::add_pointer_t<ranges::range_reference_t<R>>; using reference = ranges::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(std::declval<R>(), std::declval<Args>()...),如果该表达式是有效的。- 否则,
C(std::from_range, std::declval<R>(),std::declval<Args>()...),如果该表达式是有效的。 - 否则,
C(std::declval</*input-iterator*/>(),std::declval</*input-iterator*/>(),std::declval<Args>()...),如果该表达式是有效的。 - 否则,程序非良构。
该调用等价于 to<decltype(/*DEDUCE-EXPR*/)>(std::forward<R>(r), std::forward<Args>(args)...)。
ranges::sized_range 并且有资格被预留,则为 true。Reference 类型的一个元素可以通过成员函数调用 emplace_back、push_back、emplace 或 insert 向 Container 追加,则为 true。return [&c]<class Reference>(Reference&& ref) { if constexpr (requires { c.emplace_back(std::declval<Reference>()); }) c.emplace_back(std::forward<Reference>(ref)); else if constexpr (requires { c.push_back(std::declval<Reference>()); }) c.push_back(std::forward<Reference>(ref)); else if constexpr (requires { c.emplace(c.end(), std::declval<Reference>()); }) c.emplace(c.end(), std::forward<Reference>(ref)); else c.insert(c.end(), std::forward<Reference>(ref)); }
R 的构造中的容器定义,该范围的范围引用类型必须可转换为 T。参数
| r | - | 源范围对象 |
| args | - | 用于 (1,2) 构造范围或 (3,4) 绑定到范围适配器闭包对象的最后形参的实参列表。 |
| 类型要求 | ||
-C 必须是无 cv 限定的类类型 (1,3)
| ||
返回值
ranges::to 返回类型
成员对象
返回的对象的行为如同它没有目标对象,以及用 std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...) 构造的 std::tuple 对象 tup 一样,但返回的对象的赋值行为是未指定的,而且名字只是为了阐述。
构造函数
ranges::to (3,4) 的返回类型的行为如同它的复制/移动构造函数执行逐成员复制/移动一样。如果它的(上面指定的)所有成员对象都是可复制构造 (CopyConstructible) 的,那么它是可复制构造 (CopyConstructible) 的;否则,它是可移动构造 (MoveConstructible) 的。
成员函数 operator()
给定对象 G,它是从之前的调用 range::to</* 见下文 */>(args...) 得到的,当指代 G 的泛左值 g 在函数调用表达式 g(r) 中被调用时,会发生对存储对象的调用,如同通过
ranges::to</* 见下文 */>(r, std::get<Ns>(g.tup)...),其中:
r是必须满足input_range的源范围对象。Ns是整数包0, 1, ..., (sizeof...(Args) - 1)。g在调用表达式中是左值或者右值。因此std::move(g)(r)可以将绑定的实参移动到调用中,而g(r)则会复制。- 指定的模板实参是 (3)
C或 (4) 从类模板C推导出的类型,它必须不满足view。
如果 g 具有 volatile 限定类型,则程序非良构。
异常
仅当非视图对象的构造抛出异常时有抛出。
注解
向容器中插入元素可能涉及复制,这可能比移动更低效,因为在间接调用过程中会产生左值引用。用户可以选择使用 views::as_rvalue 来适配范围,以便它们的元素在间接调用过程中总是产生右值引用,这意味着移动。
当使用管道语法时,括号是必须的。
auto vec = r | std::ranges::to<std::vector>; // 错误
auto vec = r | std::ranges::to<std::vector>(); // OK
| 功能特性测试宏 | 值 | 标准 | 功能特性 |
|---|---|---|---|
__cpp_lib_ranges_to_container |
202202L |
(C++23) | std::ranges::to
|
__cpp_lib_ranges_reserve_hint |
202502L |
(C++26) | ranges::approximately_sized_range, ranges::reserve_hint, 以及对 std::ranges::to 的变动
|
示例
预览链接:Compiler Explorer
#include <boost/container/devector.hpp>
#include <concepts>
#include <initializer_list>
#include <list>
#include <print>
#include <ranges>
#include <regex>
#include <string>
#include <vector>
#ifndef __cpp_lib_format_ranges
#include <format>
#include <sstream>
auto print_aid(const auto& v)
{
std::ostringstream out;
out << '[';
for (int n{}; const auto& e : v)
out << (n++ ? ", " : "") << e;
out << ']';
return out;
}
template<typename T>
struct std::formatter<std::vector<T>, char>
{
template<class ParseContext>
constexpr ParseContext::iterator parse(ParseContext& ctx)
{
return ctx.begin();
}
template<class FmtContext>
FmtContext::iterator format(auto const& s, FmtContext& ctx) const
{
auto out{print_aid(s)};
return std::ranges::copy(std::move(out).str(), ctx.out()).out;
}
};
template<typename T>
struct std::formatter<std::list<T>, char>
{
template<class ParseContext>
constexpr ParseContext::iterator parse(ParseContext& ctx)
{
return ctx.begin();
}
template<class FmtContext>
FmtContext::iterator format(auto const& s, FmtContext& ctx) const
{
auto out{print_aid(s)};
return std::ranges::copy(std::move(out).str(), ctx.out()).out;
}
};
#end if
int main()
{
auto vec = std::views::iota(1, 5)
| std::views::transform([](int v){ return v * 2; })
| std::ranges::to<std::vector>();
static_assert(std::same_as<decltype(vec), std::vector<int>>);
std::println("{}", vec);
auto list = vec | std::views::take(3) | std::ranges::to<std::list<double>>();
std::println("{}", list);
}
void ctor_demos()
{
// 1.a.1) 直接初始化
{
char array[]{'a', 'b', '\0', 'c'};
// 实参类型可以转换为结果值类型:
auto str_to = std::ranges::to<std::string>(array);
// 等价于
std::string str(array);
// 结果类型不是输入范围:
auto re_to = std::ranges::to<std::regex>(array);
// 等价于
std::regex re(array);
}
// 1.a.2) from_range 构造函数
{
auto list = {'a', 'b', '\0', 'c'};
// 实参类型可以转换为结果值类型:
auto str_to = std::ranges::to<std::string>(list);
// 等价于
// std::string str(std::from_range, list);
// 结果类型不是输入范围:
[[maybe_unused]]
auto pair_to = std::ranges::to<std::pair<std::from_range_t, bool>>(true);
// 等价于
std::pair<std::from_range_t, bool> pair(std::from_range, true);
}
// 1.a.3) 迭代器对构造函数
{
auto list = {'a', 'b', '\0', 'c'};
// 实参类型可以转换为结果值类型:
auto devector_to = std::ranges::to<boost::container::devector<char>>(list);
// 等价于
boost::container::devector<char> devector(std::ranges::begin(list),
std::ranges::end(list));
// 结果类型不是输入范围:
std::regex re;
auto it_to = std::ranges::to<std::cregex_iterator>(list, re);
// 等价于
std::cregex_iterator it(std::ranges::begin(list), std::ranges::end(list), re);
}
}
输出:
[2, 4, 6, 8]
[2, 4, 6]
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
| 缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
|---|---|---|---|
| LWG 3984 | C++23 | 当 R& 未实现 viewable_range 时 ranges::to 的嵌套构造分支会导致程序非良构
|
使之良构 |
| LWG 4016 | C++23 | ranges::to 的容器插入分支涉及到使用插入迭代器
|
改为直接向容器追加元素 |
引用
- C++23 标准(ISO/IEC 14882:2024):
- 26.5.7 Range conversions [range.utility.conv]