std::ranges::to

来自cppreference.com
< cpp‎ | ranges
 
 
范围库
范围访问
范围转换器
to
(C++23)
范围原语



悬垂迭代器处理
范围概念
视图

范围工厂
适配器
范围生成器
范围适配器对象
范围适配器闭包对象
辅助项
 
在标头 <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) (C++23 起)
template< class Container, class Reference >

constexpr bool /*container-insertable*/ = requires (Container& c, Reference&& ref) {
  requires (requires { c.push_back(std::forward<Reference>(ref)); } ||
            requires { c.insert(c.end(), std::forward<Reference>(ref)); });

};
(6) (C++23 起)
template< class Reference, class C >

constexpr auto /*container-inserter*/( C& c ) {
  if constexpr (requires { c.push_back(std::declval<Reference>()); })
    return std::back_inserter(c);
  else
    return std::inserter(c, c.end());

}
(7) (C++23 起)
template< class R, class T >

concept /*container-compatible-range*/ =
  ranges::input_range<R> &&

  std::convertible_to<ranges::range_reference_t<R>, T>;
(8) (C++23 起)

范围转换函数的重载通过调用一个接受范围的构造函数、一个带有 std::from_range_t 标记的范围构造函数、一个接受迭代器-哨兵对的构造函数,或者通过将源范围的每个元素后插入到参数构造的对象中,来从第一个参数的源范围构造一个新的非视图对象。

1) 以下情况下,从 r 的元素构造类型为 C 的对象:
a) 如果 C 不满足 input_range 或者 std::convertible_to<ranges::range_reference_t<R>, ranges::range_value_t<C>>true
1) 如果 std::constructible_from<C, R, Args...>true,则像使用 直接初始化(但不是直接列表初始化)一样,从源范围 std::forward<R>(r) 和其余的函数参数 std::forward<Args>(args)... 构造一个非视图对象。
2) 否则,如果 std::constructible_from<C, std::from_range_t, R, Args...>true,则像使用 直接初始化(但不是直接列表初始化)一样,从额外的消歧标记 std::from_range、源范围 std::forward<R>(r) 和其余的函数参数 std::forward<Args>(args)... 构造一个非视图对象。
3) 否则,构造一个非视图对象,就像直接初始化(但不是直接列表初始化)从迭代器-哨兵对(ranges::begin(r) 作为迭代器和 ranges::end(r) 作为哨兵,其中迭代器和哨兵具有相同的类型。换句话说,源范围必须是一个 common_range),以及函数参数的其余部分 std::forward<Args>(args)... 构造一个类型为 C 的对象一样,如果以下所有条件都为 true
4) 否则,构造一个非视图范围对象,就像从函数参数的其余部分 std::forward<Args>(args)...直接初始化(但不是直接列表初始化)一个类型为 C 的对象,并在构造后使用以下等效调用:
if constexpr (ranges::sized_range<R> && /*reservable-container*/<C>)

  c.reserve(static_cast<ranges::range_size_t<C>>(ranges::size(r)));

ranges::copy(r, /*container-inserter*/<ranges::range_reference_t<R>>(c));

如果 R 满足 sized_range 并且 C 满足 /*reservable-container*/,则构造的类型为 C 的对象 c 能够以初始存储大小 ranges::size(r) 预留存储空间,以防止在插入新元素时进行额外的分配。r 的每个范围引用元素通过 ranges::copy 与尾部插入适配器一起尾部插入到 c 中。 上述操作在以下两个条件都为 true 时有效:

b) 否则,返回表达式等效于:
to<C>(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 则允许在范围内嵌套范围构造。

否则,程序非良构。
2)r 的元素构造一个推导出的类型的对象。

使 /*input-iterator*/ 是一个仅用于阐述的类型,它满足 老式输入迭代器 (LegacyInputIterator)

struct /*input-iterator*/ {                         // exposition only

  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;                      // not defined
  pointer operator->() const;                       // not defined
  /*input-iterator*/& operator++();                 // not defined
  /*input-iterator*/ operator++(int);               // not defined
  bool operator==(const /*input-iterator*/&) const; // not defined

};

定义 /*DEDUCE-EXPR*/ 为以下内容:

该调用等效于

to<decltype(/*DEDUCE-EXPR*/)>(std::forward<R>(r), std::forward<Args>(args)...)
3-4) 返回一个完美转发调用包装器,它也是一个范围适配器闭包对象 (RangeAdaptorClosureObject)
5) 仅用于阐述的变量模板 /*reservable-container*/<Container>true,如果它满足 ranges::sized_range 并且有资格被预留。
6) 仅用于阐述的变量模板 /*container-insertable*/<Container, Reference>true,如果 Container 可以通过成员函数调用 push_backinsert进行后插入。
7) 仅用于阐述的函数模板 /*container-inserter*/ 返回一个类型为 std::back_insert_iterator 的输出迭代器,如果成员函数 push_back 可用,否则类型为 std::insert_iterator
8) 仅用于阐述的概念 /*container-compatible-range*/ 用于在构造一个输入范围 R 和其范围引用类型必须可转换为 T 的容器的定义中。

参数

r - 源范围对象
args - 用于 (1-2) 构造一个范围或 (3-4) 绑定到范围适配器闭包对象的最后参数的参数列表。

返回值

1-2) 一个非 view 对象
3-4) 一个未指定类型的 range 适配器闭包对象,具有以下属性:

ranges::to return type

返回值从 ranges::range_adaptor_closure</*return-type*/> 继承。

成员对象

返回的对象的行为就像它没有目标对象一样,以及一个用 std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...) 构造的 std::tuple 对象 tup,除了返回的对象的赋值行为是未指定的,而且名字只是为了阐述。

构造函数

ranges::to (3-4) 的返回类型的行为就像它的复制/移动构造函数执行成员逐一复制/移动一样。如果它的所有成员对象(上面指定的)都是 可复制构造 (CopyConstructible) 的,那么它是可复制构造 (CopyConstructible) 的;否则,它是 可移动构造 (MoveConstructible) 的。

成员函数 operator()

给定一个对象 G,它是从之前的调用 range::to</* see below */>(args...) 得到的,当一个指向 G 的 glvalue g 在函数调用表达式g(r) 中被调用时,会发生存储对象的调用,就像通过

  • ranges::to</* see below */>(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 限定类型,则程序非良构。

异常

只抛出 range 对象构造时抛出的异常。

注解

向容器中插入元素可能涉及复制,这可能比移动更低效,因为在间接调用过程中会产生左值引用。用户可以选择使用 views::as_rvalue 来适配范围,以便它们的元素在间接调用过程中总是产生一个右值引用,这意味着移动。

功能特性测试 标准 备注
__cpp_lib_ranges_to_container 202202L (C++23) std::ranges::to

示例

一个到 Compiler Explorer msvc.latest 的测试

#include <algorithm>
#include <concepts>
#include <iostream>
#include <ranges>
#include <vector>
 
int main()
{
    auto vec = std::views::iota(1, 5)
             | std::views::transform([](auto const v){ return v * 2; })
             | std::ranges::to<std::vector>();
 
    static_assert(std::same_as<decltype(vec), std::vector<int>>);
 
    std::ranges::for_each(vec, [](auto const v){ std::cout << v << ' '; });
}

输出:

2 4 6 8

参阅

  • C++23 标准(ISO/IEC 14882:2023):
  • 26.5.7 Range conversions [range.utility.conv]