命名空间
变体
操作

std::ranges::to

来自 cppreference.cn
< 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 /*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 标记范围构造函数、接受迭代器-哨位对的构造函数,或者通过将源范围的每个元素后向插入到实参构造的对象中。

1)r 的元素构造类型 C 的对象,按照以下方式
1) 构造一个非视图对象,如同从源范围 std::forward<R>(r) 和剩余的函数实参 std::forward<Args>(args)... 直接初始化(但不是直接列表初始化)类型 C 的对象,如果 std::constructible_from<C, R, Args...>true
2) 否则,构造一个非视图对象,如同从额外的消歧义标签 std::from_range、源范围 std::forward<R>(r) 和剩余的函数实参 std::forward<Args>(args)... 直接初始化(但不是直接列表初始化)类型 C 的对象,如果 std::constructible_from<C, std::from_range_t, R, Args...>true
3) 否则,构造一个非视图对象,如同从迭代器-哨位对(ranges::begin(r) 作为迭代器,且 ranges::end(r) 作为哨位,其中迭代器和哨位具有相同类型。换句话说,源范围必须是共同范围),以及剩余的函数实参 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::for_each(r, /*container-appender*/(c));

(直到 C++26)

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

(自 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 起),以防止在插入新元素期间进行额外的分配。 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>>;
    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*/ 定义如下

调用等价于 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) 绑定到范围适配器闭包对象的最后参数的参数列表
类型要求
-
C 必须是 cv-非限定类类型 (1,3)

[编辑] 返回值

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

ranges::to 返回类型

成员对象

返回的对象行为如同它没有目标对象,以及使用 std::tuple 对象 tup 构造的 std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...),除了返回对象的赋值行为未指定且名称仅用于说明。

构造函数

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

成员函数 operator()

给定从先前调用 range::to</* 见下文 */>(args...) 获得的对象 G,当在函数调用表达式 g(r) 中调用指定 G 的泛左值 g 时,将发生对存储对象的调用,如同通过

  • 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>;   // Error
auto vec = r | std::ranges::to<std::vector>(); // OK
Feature-test Std 特性
__cpp_lib_ranges_to_container 202202L (C++23) 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;
    }
};
#endif
 
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:
        [[maybe_unused]]
        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]