命名空间
变体
操作

std::ranges::to

来自 cppreference.com
< cpp‎ | ranges
 
 
范围库
范围适配器
 
定义在头文件 <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 /*范围适配器闭包*/ to( Args&&... args );
(3) (从 C++23 开始)
template< template< class... > class C, class... Args >
constexpr /*范围适配器闭包*/ to( Args&&... args );
(4) (从 C++23 开始)
辅助模板
template< class Container >

constexpr bool /*可预留容器*/ =
    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 /*可追加容器*/ =
    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 /*容器追加器*/( 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 标记的范围构造函数,一个接受迭代器-哨兵对的构造函数,或通过将源范围的每个元素反向插入到参数构造的对象中,从源范围作为第一个参数创建一个新的非视图对象。

1)r 中的元素构造类型为 C 的对象,按以下步骤进行:
1) 构造一个非视图对象,就像 直接初始化 (但不是直接列表初始化)类型为 C 的对象,方法是从源范围 std::forward<R>(r) 和其余的函数参数 std::forward<Args>(args)... ,如果 std::constructible_from<C, R, Args...>true
2) 否则,构造一个非视图对象,就像 直接初始化 (但不是直接列表初始化)类型为 C 的对象,方法是从额外的消除歧义标签 std::from_range、源范围 std::forward<R>(r) 和其余的函数参数 std::forward<Args>(args)... ,如果 std::constructible_from<C, std::from_range_t, R, Args...>true
3) 否则,构造一个非视图对象,就像 直接初始化 (但不是直接列表初始化)类型为 C 的对象,方法是从迭代器-哨兵对 (ranges::begin(r) 作为迭代器和 ranges::end(r) 作为哨兵,其中迭代器和哨兵类型相同。换句话说,源范围必须是通用范围),以及其余的函数参数 std::forward<Args>(args)... ,如果以下所有条件均为 true
4) 否则,构造一个非视图范围对象,就像 直接初始化 (但不是直接列表初始化)类型为 C 的对象,方法是从其余的函数参数 std::forward<Args>(args)... ,并使用以下等效调用在构造之后执行:

if constexpr (ranges::sized_range<R> && /*reservable-container*/<C>)
    c.reserve(static_cast<ranges::range_size_t<C>>(ranges::size(r)));
ranges::for_each(r, /*container-appender*/(c));

如果 R 满足 sized_rangeC 满足 reservable-container,则构造的类型为 C 的对象 c 能够使用初始存储大小 ranges::size(r) 预留存储空间,以防止在插入新元素时进行额外的分配。 r 的每个元素都追加到 c 中。 仅当以下两个条件均为 true 时,上述操作才有效

b) 否则,返回值表达式等效于

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,则允许在范围内进行嵌套范围构造。

否则,程序格式不正确。
2)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>>;
    使用 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*/ 定义如下

该调用等效于 to<decltype(/*DEDUCE-EXPR*/)>(std::forward<R>(r), std::forward<Args>(args)...).
3,4) 返回一个完美转发调用包装器,它也是一个 RangeAdaptorClosureObject.
5) 如果它满足 ranges::sized_range 并且有资格被预留,则为 true
6) 如果可以通过成员函数调用 emplace_backpush_backemplaceinsert 将一个类型为 Reference 的元素附加到 Container,则为 true
7) 返回一个函数对象,其中对返回的函数对象的调用在表达式上等效于将一个元素附加到容器。返回表达式等效于

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));
};

8) 用于在容器的定义中构建一个输入范围 R,其范围引用类型必须可转换为 T

内容

[编辑] 参数

r - 一个源范围对象
args - 用于 (1,2) 构造范围或 (3,4) 绑定到范围适配器闭包对象的最后一个参数的 arguments 列表
类型要求
-
C 必须是 cv 无限定的类类型 (1,3)

[编辑] 返回值

1,2) 一个构造的非视图对象。
3,4) 一个类型未指定的范围适配器闭包对象,具有以下属性

ranges::to 返回类型

成员对象

返回的对象的行为就好像它没有目标对象一样,以及一个用 std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...) 构造的 std::tuple 对象 tup,只是返回对象的赋值行为未指定,并且名称仅供说明。

构造函数

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

成员函数 operator()

给定从对 range::to</* see below */>(args...) 的早期调用获得的对象 G,当在函数调用表达式 g(r) 中调用指定 G 的 glvalue g 时,会发生对存储对象的调用,就好像通过

  • 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 限定类型,则程序格式错误。

[编辑] 异常

仅在构建非视图对象时抛出异常。

[编辑] 注释

将元素插入容器可能涉及复制,这可能不如移动效率高,因为在间接调用期间会产生左值引用。用户可以选择使用 views::as_rvalue 来适配范围,以便它们的元素在间接调用期间始终产生右值引用,这意味着移动。

使用管道语法时,括号是必需的。

auto vec = r | std::ranges::to<std::vector>;   // error
auto vec = r | std::ranges::to<std::vector>(); // OK
功能测试 Std 功能
__cpp_lib_ranges_to_container 202202L (C++23) std::ranges::to

[编辑] 示例

预览链接:编译器资源管理器

#include <boost/container/devector.hpp>
#include <concepts>
#include <list>
#include <initializer_list>
#include <iostream>
#include <print>
#include <ranges>
#include <regex>
#include <string>
#include <vector>
 
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) Direct init
    {
        char array[]{'a', 'b', '\0', 'c'};
 
        // Argument type is convertible to result value type:
        auto str_to = std::ranges::to<std::string>(array);
        // Equivalent to
        std::string str(array);
 
        // Result type is not an input range:
        auto re_to = std::ranges::to<std::regex>(array);
        // Equivalent to
        std::regex re(array);
    }
 
    // 1.a.2) from_range ctor
    {
        auto list = {'a', 'b', '\0', 'c'};
 
        // Argument type is convertible to result value type:
        auto str_to = std::ranges::to<std::string>(list);
        // Equivalent to
        std::string str(std::from_range, list);
 
        // Result type is not an input range:
        auto pair_to = std::ranges::to<std::pair<std::from_range_t, bool>>(true);
        // Equivalent to
        std::pair<std::from_range_t, bool> pair(std::from_range, true);
    }
 
    // 1.a.3) iterator pair ctor
    {
        auto list = {'a', 'b', '\0', 'c'};
 
        // Argument type is convertible to result value type:
        auto devector_to = std::ranges::to<boost::container::devector<char>>(list);
        // Equivalent to
        boost::container::devector<char> devector(std::ranges::begin(list), std::ranges::end(list));
 
        // Result type is not an input range:
        std::regex re;
        auto it_to = std::ranges::to<std::cregex_iterator>(list, re);
        // Equivalent to
        std::cregex_iterator it(std::ranges::begin(list), std::ranges::end(list), re);
    }
}

输出

[2, 4, 6, 8]
[2, 4, 6]

[编辑] 缺陷报告

以下行为更改的缺陷报告已追溯应用到先前发布的 C++ 标准。

DR 应用于 已发布的行为 正确行为
LWG 3984 C++23 ranges::to 的嵌套构造分支
如果 R& 不建模 viewable_range,则导致程序格式错误。
格式正确。
LWG 4016 C++23 ranges::to 的容器插入分支涉及使用
插入迭代器
用直接追加替换
元素到容器

[编辑] 参考文献

  • C++23 标准 (ISO/IEC 14882:2024)
  • 26.5.7 范围转换 [range.utility.conv]