范围适配器

范围适配器从范围创建视图std::views 命名空间中的视图类之一)。 建议使用适配器来创建视图,而不是直接创建视图类型。 适配器是访问视图的预期方式。 与直接创建视图类型的实例相比,它们更易于使用,在某些情况下更高效。

视图是一个轻量级对象,它引用范围中的元素。 视图可以:

  • 仅包含范围中的某些元素。
  • 表示范围中元素的转换。
  • 是范围的第一个元素或仅第一 n 个元素的反向元素。
  • 将上述内容组合在一起。

视图是简单的 O(1),用于复制、分配和销毁,无论涉及多少个元素。 请考虑以下示例:

// requires /std:c++20 or later
#include <ranges>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> input =  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto divisible_by_three = [](const int n) {return n % 3 == 0;};
    auto square = [](const int n) {return n * n;};

    auto x = input | std::views::filter(divisible_by_three)
                   | std::views::transform(square);

    for (int i : x)
    {
        std::cout << i << ' ';
    }
}
0 9 36 81

第一个范围适配器 filter 提供视图,其中包含三个可见的 input 中的元素。 另一个范围适配器 transform 采用视图,其中包含可分成三部分的元素,并提供这些元素的正方形视图。

当范围适配器生成视图时,它不会产生转换范围中每个元素以生成该视图的成本。 仅当访问该元素时,才会支付在视图中处理元素的成本。

创建视图是准备将来执行工作。 在上一个示例中,创建视图不会导致发现所有元素被分成三部分或争用这些元素。 只有在访问视图中的元素时,才会发生工作。

视图的元素通常是用于创建视图的范围的实际元素。 视图通常不拥有元素;它只是引用它们,但 owning_view 例外。 更改元素会更改用于创建视图的范围中的元素。 下面的示例演示了此行为:

#include <algorithm>
#include <iostream>
#include <ranges>

int main()
{
    int input[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto even = [](const int n) { return n % 2 == 0; };
    auto x = input | std::views::filter(even); // create a view of the even elements from input

    for (int &i : x)
    {
        std::cout << i << ' '; // 0 2 4 6 8 10
    }
    std::cout << '\n'; 

    std::ranges::fill(x, 42); // changes the evens from input[] to 42
    for (int &i : input) // demonstrates that the even elements in the range are modified
    {
        std::cout << i << ' '; // // 42 1 42 3 42 5 42 7 42 9 42
    }
}

范围适配器有多种形式。 例如,有一些范围适配器允许你通过以下方式生成视图:

  • 根据谓词筛选另一个范围 (filter)。
  • 转换范围中的元素 (transform)。
  • 拆分范围 (split)。

范围适配器可以链接在一起(组合)。 这就是范围的强大和灵活性最明显的体现。 通过组合范围适配器,可以克服以前的标准模板库 (STL) 算法的核心问题,即它们不容易链接在一起。

std::views 命名空间中提供了以下范围适配器。 std::views 命名空间是 std::ranges::views 的便利别名。

范围适配器 说明
allC++20 创建引用范围及其元素的视图。
commonC++20 创建具有不同范围中的相同迭代器和 sentinel 类型的视图。
countedC++20 从指定位置开始,创建范围的前 n 个元素的视图。
dropC++20 从另一个视图创建视图,跳过前面指定的元素数。
drop_whileC++20 创建包含在符合指定条件的前导元素删除之后保留的范围元素的视图。
elementsC++20 在范围中每个元组类值中创建所选索引的视图。
emptyC++20 创建没有元素的视图。
filterC++20 创建包含符合指定条件的范围元素的视图。
iotaC++20 创建包含递增值序列的视图。
istreamC++20 基于流元素创建视图。
joinC++20 创建将多个范围的所有元素合并到单个视图中的视图。
keysC++20 创建集合中每个元组类值的第一个索引的视图。
lazy_splitC++20 根据分隔符将视图拆分为子范围。
reverseC++20 创建以倒序显示范围元素的视图。
singleC++20 创建包含一个元素的视图。
splitC++20 根据分隔符将视图拆分为子范围。
takeC++20 从另一个视图中创建前 n 个元素的视图。
take_whileC++20 创建包含符合指定条件的范围的前导元素的视图。
transformC++20 从另一个视图创建已转换元素的视图。
valuesC++20 创建集合中每个元组类值的第二个索引的视图。

在上表中,范围适配器通常描述为获取范围并生成视图。 为了精确,范围适配器具有接受以下其中一项的范围参数:

  • cv-unqualified 类型模型 view,参数为 rvalue 或可复制。
  • 将参数作为 lvalue 传递时,它必须对 range 建模并与视图保存同样长的时间。
  • 将参数作为 rvalue 传递时,例如调用 owning_view 时,它必须对 rangemovable 建模。

范围适配器函数通常是函数对象,看起来像函数调用,对可传递的类型强制实施约束。

可以将范围适配器和管道操作的结果 (|) 传递给需要函数对象的代码。 在下面的示例中,范围适配器创建的视图 split 将传递给 transform 范围适配器,就像通过函数调用一样,因为 transform 范围适配器是函数对象。

std::map<int, string> x = {{0, "Hello, world"}, {42, "Goodbye, world"}};
auto y = x | views::values | views::transform(views::split(' '));
// y is a range whose elements are ranges of whitespace-delimited strings from each value in x:
// {{"Hello", "world"}, {"Goodbye", "world"}}

all

创建范围中所有元素的视图。

template <ranges::viewable_range R>
constexpr ranges::view auto all(R&& rg) const noexcept;

参数

R
基础范围的类型。

rg
要从中创建视图的范围。

返回值

  • 如果 rg 已经是视图,则为 rg 的副本。
  • 如果 rg 为非视图 lvalue,则为引用 rgref_view。 (视图的生存期与 rg 的生存期相关。)
  • 如果 rg 为非视图 rvalue(如临时对象),或者是范围传递到 std::move 的结果,则为 owning_view

使用 std::views::all_t<decltype((rg))> 获取返回视图的类型。

备注

此范围适配器是将范围转换为视图的最佳方式。 如果按值传递范围的成本太高,则以低成本按值传递,这是从范围创建视图的一个原因。

获取范围的视图是一种有用的替代方法,用于按值传递重量级范围,因为视图创建、复制和销毁成本较低。 一个可能的例外是 owning_view,它是拥有基础范围的视图。

一般情况下,销毁视图的最坏情况对范围中的元素数具有 O(N) 复杂性。 即使使用 N 元素销毁视图的 K 副本,总复杂性仍然 O(N),因为基础范围仅销毁一次。

示例: all

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v = {1,2,3,4,5,6,7,8,9,10};
    auto myRefView = std::views::all(v); // create a ref_view of the vector
    std::cout << myRefView.size() << '\n'; // 10

    auto myOwningView = std::views::all(std::move(v)); // create an owning_view from a moved vector
    std::cout << myRefView.size() << '\n'; // outputs 0 because myOwningView now owns the elements
    std::cout << v.size() << '\n'; // outputs 0 because myOwningView now owns the elements
    std::cout << myOwningView.size(); // 10 
}
10
0
0
10

common

创建具有不同范围中的相同开始迭代器和 sentinel 类型的视图。

template <ranges::viewable_range R>
constexpr ranges::view auto common(R&& rg) const noexcept;

参数

R
基础范围的类型。

rg
要从中创建视图的范围。

返回值

  • 如果 rg 是具有相同迭代器和 sentinel 类型的范围,则 views::all(rg)
  • 如果 rg 具有不同的迭代器和 sentinel 类型,则 common_view(views::all(rg))

备注

如果 API 要求开始迭代器和结束 sentinel 具有相同的类型,而使用的视图不符合该要求(或者不知道是否符合该要求),请使用此范围适配器创建 common_view。 它保证开始迭代器的类型和结束 sentinel 的类型相同。

示例: common

// requires /std:c++20 or higher
#include <ranges>
#include <iostream>
#include <numeric>
#include <list>

int main()
{
    std::list<int> lst{1, 2, 3, 4, 5, 6, 7, 8, 9};

    auto firstFive = std::views::take(lst, 5); 
    // firstFive.begin(), firstFive.end() have different types: counted_iterator versus default_sentinel
    // auto r = std::accumulate(firstFive.begin(), firstFive.end(), 0); // Error: accumulate() requires firstFive.begin() and firstFive.end() types to be the same.
    
    auto common = std::views::common(firstFive); // create a common_view that has the same begin/end iterator types
    std::cout << std::accumulate(common.begin(), common.end(), 0); // Now you can call the API because the iterator types are the same. Outputs 15 (1+2+3+4+5) 
}
15

counted

从指定位置开始,创建范围的前 count 个元素的视图。

template<class Iterator>
constexpr auto counted(Iterator&& it, iter_difference_t<Iterator> count);

参数

DifferenceType
计数的类型。

Iterator
迭代器的类型。

count
要包括在视图中的元素数。 必须为非负值。

  • 如果 count == 0,则返回空 span
  • 如果 count 大于区域中的元素数,则行为未定义。

it
要作为开始元素的范围中元素的迭代器。 迭代器指向的元素包含在创建的视图中。

返回值

如果 it 为连续存储其元素的数组、向量和其他容器的 contiguous_iterator,则返回 span。 否则将返回 subrange

注解

包含的元素为 [it, count)

创建视图后,视图中的元素数保持不变,即使从更改创建该视图的范围也是如此。 但是,如果基础范围发生更改,则从视图中访问元素可能会导致未定义的行为。

示例: counted

// requires /std:c++20 or later
#include <algorithm>
#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto pos5 = std::ranges::find(v, 5);
    auto countedView = std::views::counted(pos5, 5);
    for (auto e : countedView) // outputs 5 6 7 8 9
    {
        std::cout << e << ' ';
    }
    std::cout << '\n';

    // You can pass the range directly if it supports input_or_output_iterator, in which case
    // the count starts from the first element
    const char chars[] = { 'H','i',' ','t','h','e','r','e' };
    for (char c : std::views::counted(chars, 2))
    {
        std::cout << c; // outputs Hi
    }
}
5 6 7 8 9
Hi

drop

创建其中排除范围前 n 个元素的视图。

1) template<ranges::viewable_range R>
constexpr ranges::view auto drop(R&& rg, ranges::range_difference_t<R> count);

2) template<class DifferenceType>
constexpr /* range closure object */ drop(DifferenceType&& count);

参数

DifferenceType
描述要跳过的元素数的类型。

count
要从 rg 前面删除的元素数。 必须为非负值。

  • 如果 count == 0,则返回 rg 中的所有元素。
  • 如果 count 大于 rg 中的元素数,则返回空视图。

R
范围的类型。

rg
用于创建视图的范围。

返回值

基础范围的视图,不包括从前面删除的指定元素数。

如果指定要删除的元素多于基础范围中的元素,则会返回 empty_view

返回的视图通常是(但并非总是)drop_view 的专用化。 即:

注解

创建视图后,视图中的元素数保持不变,即使从更改创建它的视图也是如此。 但是,如果基础视图发生更改,则访问返回的视图中的元素可能会导致未定义的行为。

droptake 相反。

前面显示为 "2)" 的代码可与管道语法一起使用:collection | drop(5)。 或者,它可以与函数调用语法一起使用:drop(collection, 5)drop(5)(collection)

示例: drop

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v{1, 2, 3, 4, 5};
    auto newView = std::views::drop(v, 3);
    for (auto e : newView) // 4 5
    {
        std::cout << e << ' ';
    }
    std::cout << '\n';

    auto numbers = std::views::iota(0) | std::views::take(10); // build a view of 10 integers
    auto latterHalf = numbers | std::views::drop(5);
    for (auto i : latterHalf)
    {
        std::cout << i << ' '; // 5 6 7 8 9
    }
}
4 5
5 6 7 8 9

drop_while

创建包含在符合指定条件的前导元素删除之后保留的范围元素的视图。

1) template<ranges::viewable_range R, class P>
constexpr ranges::view auto drop_while(R&& rg, P&& predicate);

2) template<class P>
constexpr /*range adaptor closure*/ drop_while(P&& predicate);

参数

R
范围的类型。

predicate
用于确定要从范围中删除哪些前导元素的条件。

rg
要从中创建视图的基础范围。

返回值

由删除匹配谓词的前导元素时保留的元素组成的 drop_while_view

备注

只要谓词返回 false,立即停止从 rg 删除元素。

drop_whiletake_while 相反。

前面显示为 "2)" 的代码可与管道语法一起使用:collection | drop_while(predicate)。 或者,它可以与函数调用语法一起使用:drop_while(collection, predicate)drop_while(predicate)(collection)

示例: drop_while

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

void print(auto&& v)
{
    for (auto&& x : v)
    {
        std::cout << x << ' ';
    }
    std::cout << '\n';
}

int main()
{
    std::vector<int> v{ 0, 1, 2, 3, -4, 5, 6 };
    auto myView = std::views::drop_while(
        v,
        [](int i) {return i >= 0; });
    print(myView); // -4 5 6

    auto myView2 = v | std::views::drop_while(
        [](int i) {return i < 5; });
    print(myView2); // 5 6
}
-4 5 6
5 6

elements

创建 elements_view,这是范围中每个元组类值中所选索引的视图。 例如,给定一系列 std::tuple<string, int> 值后,创建包含每个元组中所有 string 元素的 elements_view

template<ranges::viewable_range R, size_t N>
constexpr ranges::view auto elements<N>(R&& rg);

参数

N
要从每个元组类值中选择包含在视图中的元素的索引。

R
基础范围的类型。

rg
要用于创建视图的元组类值的范围。

返回值

由集合中每个元组类值的所选索引组成的 elements_view

示例: elements

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <map>
#include <string>

int main()
{
    std::map<std::string, int> cpp_standards
    {
        {"C++98", 1998},
        {"C++03", 2003},
        {"C++11", 2011},
        {"C++14", 2014},
        {"C++17", 2017},
        {"C++20", 2020}
    };

    // Create an elements_view of all the string elements from each tuple
    for (int const year : std::views::elements<1>(cpp_standards))
    {
        std::cout << year << ' '; // 2003 2011 2014 2017 1998 2020
    }
    std::cout << '\n';

    // Another way, using |: create an elements_view of all the int elements from each tuple
    for (auto&& name : cpp_standards | std::views::elements<0>)
    {
        std::cout << name << ' '; // C++03 C++11 C++14 C++17 C++98 c++20
    }
}
2003 2011 2014 2017 1998 2020
C++03 C++11 C++14 C++17 C++98 c++20

empty

创建 empty_view,这是没有元素的视图。

template<class T>
inline constexpr empty_view<T> empty{};

参数

T
视图中元素的类型。 即使没有元素,视图也需要元素类型。

返回值

empty_view

备注

调用需要视图但不需要处理其任何元素的代码时,empty_view 很有用。

示例: empty

// requires /std:c++20 or higher
#include <ranges>
#include <iostream>

int main()
{
    auto anEmptyView = std::views::empty<int>;
    bool isNotEmpty = (bool)anEmptyView;
    std::cout << boolalpha << isNotEmpty << "\n"; // false
}
false

filter

创建包含符合指定条件的范围元素的视图。

1) template<ranges::viewable_range R, class P>
    requires {filter_view(forward<R>(rg), forward<P>(predicate));}
constexpr ranges::view auto filter(R&& rg, P&& predicate);

2) template<class P>
constexpr /*range adaptor closure*/ filter(P&& predicate);

参数

P
谓词的类型。

predicate
用于确定哪些元素保留在范围中的条件。

R
基础范围的类型。

rg
要从中创建视图的范围。

返回值

包含与谓词匹配的范围元素的 filter_view

注解

为了提高效率,在 filtertransform 与管道 | 一起使用时,先执行 filter 以便 transform 仅有你要保留的元素。

前面显示为 "2)" 的代码可与管道语法一起使用:collection | filter(predicate)。 或者,它可以与函数调用语法一起使用:filter(collection, predicate)filter(predicate)(collection)

示例: filter

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

void print(auto&& v)
{
    for (auto&& x : v)
    {
        std::cout << x << ' ';
    }
    std::cout << '\n';
}

int main()
{
    std::vector<int> v{0, 1, 2, 3, -4, 5, 6};
    auto myView = std::views::filter(v, [](int i) {return i < 5; });
    print(myView); // 0 1 2 3 -4

    auto myView2 = v | std::views::filter([](int i) {return i < 5; }); // pipe syntax
    print(myView2); // 0 1 2 3 -4
}
0 1 2 3 -4
0 1 2 3 -4

iota

创建包含递增值序列的视图。 序列可以绑定或不绑定。

template<class V>
constexpr ranges::view auto iota(V&& startValue); // create an unbounded sequence of incrementing values

template<class V, class E>
constexpr ranges::view auto iota(V&& startValue, E&& endValue); // create a bounded sequence of incrementing values

参数

E
结束值的类型。

S
起始值的类型。

startValue
序列中的第一个值。

endValue
此值是序列中最后一个值之前的一个值。 例如,std::views::iota(0, 5) 生成具有值 0,1,2,3,4 的视图。

返回值

递增值的序列 iota_view

备注

对于未绑定的序列,在达到其数据类型的最大值后,行为是未定义的。

示例: iota

// requires /std:c++20 or later
#include <ranges>
#include <iostream>

void print(auto&& v)
{
    for (auto&& x : v)
    {
        std::cout << x << ' ';
    }
    std::cout << '\n';
}

int main()
{
    // create an iota view with its range adaptor (preferred)
    print(std::views::iota(0, 5)); // outputs 0 1 2 3 4
    
    // create an iota_view class directly
    std::ranges::iota_view letters{'a', 'f'};
    print(letters); // a b c d e
}
0 1 2 3 4
a b c d e

istream

基于流元素创建视图。

template <class Val>
views::istream<Val>(str);

参数

str
一个流对象。 其类型派生自 std::basic_istream 的专用化。

Val
要从流中提取的元素的类型。

返回值

一个 basic_istream_view

此范围适配器等效于 ranges::basic_istream_view<Val, typename U::char_type, typename U::traits_type>(str),其中 Ustr 的类型。

示例: istream

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <sstream>
#include <vector>

int main()
{
    std::istringstream doubles{"1.1 2.2 3.3 4.4 5.5"};
    for (const auto& elem : std::views::istream<double>(doubles))
    {
        std::cout << elem << ' '; // 1.1 2.2 3.3 4.4 5.5
    }
}
1.1 2.2 3.3 4.4 5.5

join

创建将多个范围的所有元素合并到单个视图中的视图。

1) template <ranges::viewable_range R>
[[nodiscard]] constexpr ranges::view auto join(R&& rg) const noexcept;

2) inline constexpr /*range adaptor closure*/ join();

参数

R
基础范围的类型。

rg
要从中创建视图的范围。

返回值

包含基础范围中所有范围的元素的 join_view

示例: join

#include <iostream>
#include <vector>
#include <ranges>
#include <string>

int main()
{
    // a range of two ranges
    std::vector<std::string> rangeOfRanges[2]{{"C++20", "contains:"}, {"ranges", "modules", "concepts & more."}};

    for (const auto& elem : std::views::join(rangeOfRanges))
    {
        std::cout << elem << ' ';
    }
}
C++20 contains: ranges modules concepts & more.

注解

前面显示为 "2)" 的代码可与管道语法一起使用:collection | join。 或者,它可以与函数调用语法一起使用:join(collection)

keys

创建集合中每个元组类值的第一个索引的 keys_view。 这可用于从关联容器中提取关键值。 例如,给定 std::tuple<string, int> 的范围后,创建一个视图,其中包含每个元组中的所有 string 元素。

template <ranges::viewable_range R>
constexpr auto keys(R&& rg);

参数

R
基础范围的类型。

返回值

由范围中每个元组类值的第一个索引组成的 keys_view

示例: keys

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <map>
#include <string>
#include <vector>

int main()
{
    // ========== extract keys from a map

    std::map<std::string, int> cpp_standards
    {
        {"C++98", 1998},
        {"C++03", 2003},
        {"C++11", 2011},
        {"C++14", 2014},
        {"C++17", 2017},
        {"C++20", 2020}
    };

    // Extract all of the keys from the map
    for (std::string standards : std::views::keys(cpp_standards))
    {
        std::cout << standards << ' '; // C++03 C++11 C++14 C++17 C++98 C++20
    }
    std::cout << '\n';

    // ========== Extract keys from a pair

    std::vector<std::pair<std::string, int>> windows
    {
        {"Windows 1.0", 1985},
        {"Windows 2.0", 1987},
        {"Windows 3.0", 1990},
        {"Windows 3.1", 1992},
        {"Windows NT 3.1", 1993},
        {"Windows 95", 1995},
        {"Windows NT 4.0", 1996},
        {"Windows 95", 1995},
        {"Windows 98", 1998},
        {"Windows 1.0", 1985},
        {"Windows 2000", 2000}
    };
    
    // Another way to call the range adaptor is by using '|'
    for (std::string version : windows | std::views::keys)
    {
        std::cout << version << ' '; // Windows 1.0 Windows 2.0 Windows 3.0 ...
    }
}
C++03 C++11 C++14 C++17 C++98 C++20
Windows 1.0 Windows 2.0 Windows 3.0 Windows 3.1 Windows NT 3.1 Windows 95 Windows NT 4.0 Windows 95 Windows 98 Windows 1.0 Windows 2000

lazy_split

根据分隔符将范围拆分为子范围。 分隔符可以是单个元素或元素视图。

1) template<viewable_range R, class Pattern>
constexpr view auto lazy_split(R&& rg, Pattern&& delimiter);

2) template<class Pattern>
constexpr /*range adaptor closure*/ lazy_split(Pattern&& delimiter);

参数

delimiter
单个值或指定拆分范围的位置的值序列。

Pattern
分隔符的类型。

R
要拆分的范围的类型。

rg
要拆分的范围。

返回值

包含一个或多个子范围的 lazy_split_view,是拆分 delimiter 上原始范围的结果。

备注

分隔符不是结果的一部分。 例如,如果拆分值 2 上的范围 1,2,3,则会收到两个子范围:13

相关的适配器是 split。 [split_view](split-view-class.md) and lazy_split_view` 之间的主要区别是:

视图 可以拆分 const 范围 范围迭代器
split_view 支持 forward_range 或更高版本
lazy_split_view input_range 或更高版本

首选 split_view,因为效率更高,除非必须拆分 const 范围。

前面显示为 "2)" 的代码可与管道语法一起使用:collection | lazy_split(delimiter)。 或者,它可以与函数调用语法一起使用:lazy_split(collection, delimiter)lazy_split(delimiter)(collection)

示例: lazy_split

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> rg{1, 2, 3, 1, 2, 3, 4, 5, 6};

    // split on a single element
    for (const auto& sub : rg | std::views::split(3))
    {
        // outputs:
        // 1 2
        // 1 2
        // 4 5 6
        for (const auto& elem : sub)
        {
            std::cout << elem << ' ';
        }
        std::cout << '\n';
    }

    // split on a sequence of elements
    int delimiters[] = {2, 3};
    for (const auto& subrange : std::views::split(rg, delimiters))
    {
        // outputs 1 1 4 5 6
        for (auto& i : subrange)
        {
            std::cout << i << " ";
        }
    }
}
1 2
1 2
4 5 6
1 1 4 5 6

reverse

创建以相反顺序显示范围元素的视图。

1) template<viewable_range R>
constexpr ranges::view auto reverse(R&& rg);

2) inline constexpr /*range adaptor closure*/ reverse();

参数

R
要反向的基础范围的类型。

rg
要反向的范围。

返回值

以相反顺序显示基础范围的元素的视图。 返回的视图通常是(但并非总是)reverse_view 的专用化。 即:

  • 如果 Vreverse_view 的专用化,则结果是参数的基础视图。 双反向是 no-op(无操作)。
  • 如果 V 具有窗体 subrange<reverse_iterator<I>, reverse_iterator<I>>,则结果是未包装的迭代器的 subrange。 双反向是 no-op。
  • 否则结果为 reverse_view

备注

前面显示为 "2)" 的代码可与管道语法一起使用:collection | reverse。 或者,它可以与函数调用语法一起使用:reverse(collection)

示例: reverse

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v{ 0, 1, 2, 3, -4, 5, 6 };
    auto rv = v | std::views::reverse; // using the pipe syntax  

    for (auto &&e : rv) // outputs 6 5 -4 3 2 1 0
    {
        std::cout << e << ' ';
    }
    std::cout << '\n';

    // using the range adaptor without using the pipe syntax
    auto rv2 = std::views::reverse(v);
    for (auto &&e : rv2) // outputs 6 5 -4 3 2 1 0
    {
        std::cout << e << ' ';
    }
}
6 5 -4 3 2 1 0
6 5 -4 3 2 1 0

single

创建 single_view,这是包含一个元素的视图。

template<class T>
constexpr ranges::view auto single(T&& t);

参数

T
视图中元素的类型。

t
要在视图中存储的元素的值。

返回值

包含 tsingle_view

注解

此视图适用于测试调用需要与包含至少一个元素的视图一起提供的代码。

示例: single

// requires /std:c++20 or higher
#include <ranges>
#include <string>
#include <tuple>
#include <iostream>

int main()
{
    auto sv = std::views::single(7);
    std::cout << sv.front() << " " << *sv.data() << "\n"; // 7 7
    
    auto sv2 = std::views::single(<std::tuple<double, std::string>{6502, "8-bit"});
    std::cout << std::get<0>(sv2[0]) << " " << std::get<1>(sv2[0]) << "\n"; // 6502 8-bit
}
7 7
6502 8-bit

split

根据分隔符将视图拆分为子范围。 分隔符可以是单个元素或元素序列。

1) template<viewable_range R, class Pattern>
constexpr view auto split(R&& rg, Pattern&& delimiter);

2) template<class Pattern>
constexpr /*range adaptor closure*/ split(Pattern&& delimiter);

参数

delimiter
单个值或指定拆分范围的位置的值序列。

Pattern
分隔符的类型。

R
要拆分的基础范围的类型。

rg
要拆分的范围。

返回值

包含一个或多个子范围的 split_view

注解

分隔符不是结果的一部分。 例如,如果拆分值 2 上的范围 1,2,3,则会收到两个子范围:13

相关的适配器是 lazy_splitsplit_viewlazy_split_view 之间的主要区别包括:

查看 可以拆分 const 范围 范围类型
split_view 支持 forward_range 或更高版本
lazy_split_view 支持 input_range 或更高版本

首选 split_view,因为效率更高,除非必须拆分 const 范围。

前面显示为 "2)" 的代码可与管道语法一起使用:collection | split(delimiter)。 或者,它可以与函数调用语法一起使用:split(collection, 5)split(5)(collection)

示例: split

// requires /std:c++20 or later
#include <ranges>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> rg{ 1, 2, 3, 1, 2, 3, 4, 5, 6 };

    // split on a single element, 3
    for (const auto& sub : rg | std::views::split(3))
    {
        // This prints out:
        // 1,2
        // 4,5,6
        for (const auto& elem : sub)
        {
            std::cout << elem << ' ';
        }
        std::cout << '\n';
    }

    // split on a sequence of elements, 2,3
    int delimiters[] = {2, 3};
    for (const auto& subrange : std::views::split(rg, delimiters))
    {
        // outputs 1 1 4 5 6
        for (auto& i : subrange)
        {
            std::cout << i << " ";
        }
    }
}
1 2
1 2
4 5 6
1 1 4 5 6

take

创建包含从范围前面获取的指定数量的元素的视图。

1) template<ranges::viewable_range R>
constexpr ranges::view auto take(R&& rg, ranges::range_difference_type<R> count);

2) template<class DifferenceType>
constexpr /*range adaptor closure*/ take(DifferenceType&& count); 

参数

R
基础范围的类型。

rg
要从中创建视图的范围。

count
要从 rg 前面获取的元素数。

返回值

返回的视图通常是(但并非总是)take_view 的专用化。 具体而言:

备注

如果指定要接受的元素数多于 rg 中的元素,则将接受所有元素。

takedrop 相反。

前面显示为 "2)" 的代码可与管道语法一起使用:collection | take(5)。 或者,它可以与函数调用语法一起使用:take(5, collection)take(5)(collection)

示例: take

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::string s{ "abcdefg" };
    auto myView = std::views::take(s, 3);
    for (auto c : myView)
    {
        std::cout << c << ' '; // a b c
    }

    std::cout << std::endl;

    for (auto c : s | std::views::take(3)) // pipe syntax
    {
        std::cout << c << ' '; // a b c
    }
}
a b c
a b c

take_while

创建包含符合指定条件的范围的前导元素的视图。

1) template<ranges::viewable_range R, class P>
constexpr ranges::view auto take_while(R&& rg, P&& predicate);

2) template<class P>
constexpr /*range adaptor closure*/ take_while(P&& predicate);

参数

P
谓词的类型。

predicate
用于确定要从范围中复制哪些前导元素的条件。

R
基础范围的类型。

rg
要从中创建视图的范围。

返回值

由满足范围中指定条件的前 count 个元素组成的 take_while_view

备注

在谓词返回 false 或范围耗尽元素后,停止从 rg 获取元素。

take_whiledrop_while 相反。

前面显示为 "2)" 的代码可与管道语法一起使用:collection | take_while(pred)。 或者,它可以与函数调用语法一起使用:take_while(collection, pred)take_while(pred)(collection)

示例: take_while

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

void print(auto&& v)
{
    for (auto&& x : v)
    {
        std::cout << x << ' ';
    }
    std::cout << '\n';
}

int main()
{
    std::vector<int> v{ 0, 1, 2, 3, -4, 5, 6 };
    auto myView = std::views::take_while(
        v, [](int i) {return i >= 0; });
    print(myView); // 0 1 2 3

    print(v | std::views::take_while( // 0 1 2 3 -4
        [](int i) {return i < 5; })); // pipe syntax
}
0 1 2 3
0 1 2 3 -4

transform

创建元素视图,其中每个元素都是指定区域中元素的转换。

1) template<viewable_range R, class F>
constexpr ranges::view auto transform(R&& rg, F&& fun);

2) template<class F>
constexpr /*range adaptor closure*/ transform(F&& fun);

参数

F
要转换元素的函数对象的类型。

R
基础范围的类型。

fun
转换元素的函数。

rg
要从中创建视图的范围。

返回值

包含 rg 的已转换元素的 transform_view

备注

为了提高效率,在 filtertransform 组合时,先执行 filter,以便 transform 仅有你要保留的元素。

前面显示为 "2)" 的代码可与管道语法一起使用:collection | transform(fun)。 或者,它可以与函数调用语法一起使用:transform(collection, fun)transform(fun)(collection)

示例: transform

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

void print(auto&& v)
{
    for (auto&& x : v)
    {
        std::cout << x << ' ';
    }
    std::cout << '\n';
}

int main()
{
    std::vector<int> v{0, 1, 2, 3, -4, 5, 6};
    auto myView = std::views::transform(v, [](int i) {return i * 2; });
    print(myView); // 0 2 4 6 -8 10 12

    print(v | std::views::transform( // 0 2 4 6 -8 10 12
        [](int i) {return i * 2; })); // pipe syntax
}
0 2 4 6 -8 10 12
0 2 4 6 -8 10 12

values

创建由集合中每个元组类值的第二个索引组成的 values_view。 这可用于创建关联容器中值的视图。 例如,给定一系列 std::tuple<string, int> 值后,创建一个视图,其中包含每个元组中的所有 int 元素。

template <range::viewable_range R>
constexpr ranges::view auto values(R&& rg);

参数

R
基础范围的类型。

rg
元组类值的基础范围。

返回值

从范围中每个元组类值的第二个索引生成的 values_view

示例: values

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <map>
#include <string>
#include <vector>

int main()
{
    // ========== working with a map

    std::map<std::string, int> cpp_standards
    {
        {"C++98", 1998},
        {"C++03", 2003},
        {"C++11", 2011},
        {"C++14", 2014},
        {"C++17", 2017},
        {"C++20", 2020}
    };

    // Extract all of the years from the map
    for (int years : std::views::values(cpp_standards))
    {
        std::cout << years << ' '; // 2003 2011 2014 2017 1998 2020
    }
    std::cout << '\n';

    // ========== working with pairs

    std::vector<std::pair<std::string, int>> windows
    {
        {"Windows 1.0", 1985},
        {"Windows 2.0", 1987},
        {"Windows 3.0", 1990},
        {"Windows 3.1", 1992},
        {"Windows NT 3.1", 1993},
        {"Windows 95", 1995},
        {"Windows NT 4.0", 1996},
        {"Windows 95", 1995},
        {"Windows 98", 1998},
        {"Windows 1.0", 1985},
        {"Windows 2000", 2000}
    };

    // Another way to call the range adaptor by using '|'
    // Create a values_view that contains the year from each pair
    for (int years : windows | std::views::values)
    {
        std::cout << years << ' '; // 1985 1987 1990 1992 ...
    }
}
2003 2011 2014 2017 1998 2020
1985 1987 1990 1992 1993 1995 1996 1995 1998 1985 2000

范围适配器类型别名

all_t

提供 all 返回的视图的类型。

template <ranges::viewable_range R>
using all_t = decltype(views::all(std::declval<R>()));

参数

R
基础范围的类型。

返回值

all 返回的视图的类型:decltype(views::all(std::declval<R>()))

示例: all_t

#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v = {1,2,3,4,5,6,7,8,9,10};
    auto myView = std::views::all(v);
    std::views::all_t<decltype((v))> &viewType = myView;
}

另请参阅

<ranges>
<ranges> 概念
视图类