C++ 范围与视图

最后更新: 2021-10-01 09:45:43 +0800 +0800
原文以英文撰写。您正在阅读AI翻译版本。

ranges library separates the ability 到 iterate 来自 ownership, traversal strength, 和 lazy transformation. 一个 range supplies iteration boundaries; a view 是 a range suited 到 adaptor pipelines; an adaptor builds a new view whose elements 是 evaluated 当 iteration requests them.

cpp
#include <ranges>
#include <vector>

std::vector<int> source{1, 2, 3, 4, 5};
auto even_squares = source
    | std::views::filter([](int value) { return value % 2 == 0; })
    | std::views::transform([](int value) { return value * value; });

Constructing even_squares does 不 transform every element. Iteration invokes the predicate 和 transformation, so changes 到 source 在……之前 iteration 可以 be observed subject 到 the 源码 container’s invalidation rules.

核心概念 #

A type models std::ranges::range 当 std::ranges::begin 和 std::ranges::end 是 valid 用于 an lvalue 的 type. Iterator 和 sentinel types need 不 be the same. concept establishes 仅 iteration 是 possible; it says nothing about ownership, size, multipass traversal, contiguity, 或 iterator lifetime.

cpp
template<class T>
concept range = requires(T& value) {
    std::ranges::begin(value);
    std::ranges::end(value);
};

standard’s definition has equivalent intent but 使用 its own exposition-only details. std::vector<int> 和 std::vector<int>& both model range.

std::ranges::view refines range 用于 lightweight, adaptable range objects. Its syntactic definition combines range, std::movable, 和 std::ranges::enable_view; the important requirements 是 semantic. 一个 view 必须 have constant-time move construction, 和 its copying, moving, assignment, 和 destruction requirements prevent an adaptor pipeline 来自 unexpectedly doing element-wise work. 一个 std::vector 是 a range but 不 a view because copying its elements 是 不 constant time. std::string_view, std::ranges::ref_view, 和 many 生成的 ranges 是 views.

A program-defined view generally derives 来自 std::ranges::view_interface 或 std::ranges::view_base. view_interface<D> 是 a CRTP helper conditionally supplies members such 作为 empty, front, back, data, size, 和 indexing 仅 当 D has the required range capabilities. 一个 program may specialize enable_view 到 true 仅 用于 a cv-unqualified program-defined type actually satisfies the view requirements; the specialization 是 a semantic promise, 不 a constraint bypass.

viewable_range 和 views::all #

viewable_range answers a different question: whether a particular range expression 可以 be converted 到 a view. It admits constructible views 和 suitable lvalue 或 movable non-view inputs, 同时 excluding problematic initializer-list cases. specification has evolved since C++20, so application 代码 应当 使用 the concept rather than reproduce its definition.

std::views::all 是 the conversion boundary. Conceptually, it preserves a constructible input view, wraps a non-view lvalue in ref_view, 和 wraps an eligible non-view rvalue in owning_view.

cpp
std::vector<int> values{1, 2, 3};
auto borrowed = std::views::all(values);              // ref_view
static_assert(std::ranges::view<decltype(borrowed)>);

auto owned = std::views::all(std::vector<int>{1, 2}); // owning_view

A ref_view does 不 extend the 源码 object’s lifetime. Returning a view refers 到 a local container 是 therefore erroneous. Not every view 是 non-owning: owning_view owns its base range 和 single_view stores its value. Determine lifetime 来自 the concrete view 和 the 源码 expression, 不 来自 the word “view.”

borrowed_range 是 another independent property. It permits an algorithm 到 返回 iterators 来自 an rvalue range rather than std::ranges::dangling, because iterators 来自 concrete range 可以 survive destruction 的 the range object. It does 不 mean every iterator 是 perpetually valid; container mutation 和 the lifetime 的 any underlying storage still matter.

工厂与适配器 #

Factories create 源码 views, 同时 adaptors transform an existing viewable_range.

FacilityKindStandard Baseline
views::empty<T>Empty 源码 viewC++20
views::single(value)One stored 值C++20
views::iota(first, bound)Incrementing 生成的 sequenceC++20
views::istream<T>(stream)Lazily extracted stream valuesC++20
views::repeat(value, bound)Repeated 值C++23
views::cartesian_product(...)Cartesian productC++23
views::counted(iterator, count)Counted 源码 rangeC++20

views::transform, filter, take, drop, reverse, split, lazy_split, join, common, elements, keys, 和 values 是 C++20 adaptors. C++23 adds adaptors including adjacent, slide, chunk, stride, zip, zip_transform, join_with, 和 as_const. Availability depends 在 both the selected language mode 和 the standard library’s implementation.

An adaptor 对象 可以 be invoked directly 或 partially applied:

cpp
auto doubled = std::views::transform(source, [](int value) {
    return value * 2;
});

auto double_each = std::views::transform([](int value) {
    return value * 2;
});
auto also_doubled = double_each(source);

partially applied 结果 是 a range adaptor closure object. For a closure closure 和 viewable_range range, range | closure has the effect 的 closure(range). Closures 可以 compose:

cpp
auto useful = std::views::filter(is_valid) | std::views::transform(normalize);
auto result = source | useful;

C++23 提供 std::ranges::range_adaptor_closure 用于 portable user-defined closures. Before C++23, implementation-internal closure bases were 不 application interfaces.

范围属性与工具 #

Refinement concepts describe independent properties 的 a range:

ConceptMeaning
input_rangeSupports single-pass input traversal.
forward_rangeSupports multipass forward traversal.
bidirectional_rangeSupports backward traversal.
random_access_rangeSupports constant-time indexed iterator movement.
contiguous_rangeElements occupy contiguous storage.
sized_rangeranges::size 是 valid,使用 amortized constant complexity.
common_rangeIterator 和 sentinel have the same type.
borrowed_rangeRvalue-origin iterators need 不 dangle solely 来自 range destruction.

Traversal concepts refine one another 来自 input 通过 contiguous. sized_range, common_range, 和 borrowed_range describe separate dimensions. C++23 adds constant_range; 使用 it 仅 当 the project baseline 提供 it.

Customization-point objects handle arrays, members, 和 ADL correctly in generic code:

cpp
auto first = std::ranges::begin(source);
auto last = std::ranges::end(source);
auto count = std::ranges::size(source);

Other utility objects include cbegin, cend, rbegin, rend, empty, data, cdata, 和 ssize. Prefer them 到 assuming a .begin() member.

Range type aliases preserve iterator 和 proxy-reference behavior:

cpp
template<std::ranges::range R>
using iterator_t = std::ranges::iterator_t<R>;

template<std::ranges::range R>
using value_t = std::ranges::range_value_t<R>;

template<std::ranges::range R>
using reference_t = std::ranges::range_reference_t<R>;

Related aliases include sentinel_t, range_difference_t, range_size_t, 和 range_rvalue_reference_t. Const-oriented aliases such 作为 const_iterator_t 和 range_const_reference_t have C++23 availability constraints.

算法、投影与物化 #

Range algorithms accept ranges directly 和 可以 apply a projection 在……之前 their main operation. 一个 projection changes how one algorithm observes an element; a transform view changes what iteration yields.

cpp
struct Record {
    int id;
    std::string name;
};

std::ranges::sort(records, {}, &Record::name);

empty comparator selects the default ordering, 同时 the projection supplies member names 到 it. Projections do 不 materialize transformed values.

Materialize a view 当 an owning result, stable traversal 在……之后 a 源码 change, 或 an eager boundary 是 required. 在 C++23, std::ranges::to expresses the conversion directly:

cpp
auto output = std::ranges::to<std::vector<int>>(even_squares);

On a C++20 baseline, construct a compatible container 来自 the view’s iterator 和 sentinel 仅 当 container 支持 the pair, 或 insert 通过 its appropriate range/iterator interface. Materialization 是 the point performs the lazy work 和 establishes a separate result; it 应当 be deliberate rather than an accidental side effect 的 choosing a container API.

评论

在 GitHub 上阅读或评论(发表评论需要登录)