范围适配器
范围适配器从范围创建视图(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
}
}
范围适配器有多种形式。 例如,有一些范围适配器允许你通过以下方式生成视图:
范围适配器可以链接在一起(组合)。 这就是范围的强大和灵活性最明显的体现。 通过组合范围适配器,可以克服以前的标准模板库 (STL) 算法的核心问题,即它们不容易链接在一起。
std::views
命名空间中提供了以下范围适配器。 std::views
命名空间是 std::ranges::views
的便利别名。
范围适配器 | 说明 |
---|---|
all C++20 |
创建引用范围及其元素的视图。 |
common C++20 |
创建具有不同范围中的相同迭代器和 sentinel 类型的视图。 |
counted C++20 |
从指定位置开始,创建范围的前 n 个元素的视图。 |
drop C++20 |
从另一个视图创建视图,跳过前面指定的元素数。 |
drop_while C++20 |
创建包含在符合指定条件的前导元素删除之后保留的范围元素的视图。 |
elements C++20 |
在范围中每个元组类值中创建所选索引的视图。 |
empty C++20 |
创建没有元素的视图。 |
filter C++20 |
创建包含符合指定条件的范围元素的视图。 |
iota C++20 |
创建包含递增值序列的视图。 |
istream C++20 |
基于流元素创建视图。 |
join C++20 |
创建将多个范围的所有元素合并到单个视图中的视图。 |
keys C++20 |
创建集合中每个元组类值的第一个索引的视图。 |
lazy_split C++20 |
根据分隔符将视图拆分为子范围。 |
reverse C++20 |
创建以倒序显示范围元素的视图。 |
single C++20 |
创建包含一个元素的视图。 |
split C++20 |
根据分隔符将视图拆分为子范围。 |
take C++20 |
从另一个视图中创建前 n 个元素的视图。 |
take_while C++20 |
创建包含符合指定条件的范围的前导元素的视图。 |
transform C++20 |
从另一个视图创建已转换元素的视图。 |
values C++20 |
创建集合中每个元组类值的第二个索引的视图。 |
在上表中,范围适配器通常描述为获取范围并生成视图。 为了精确,范围适配器具有接受以下其中一项的范围参数:
cv-unqualified
类型模型view
,参数为 rvalue 或可复制。- 将参数作为 lvalue 传递时,它必须对
range
建模并与视图保存同样长的时间。 - 将参数作为 rvalue 传递时,例如调用
owning_view
时,它必须对range
和movable
建模。
范围适配器函数通常是函数对象,看起来像函数调用,对可传递的类型强制实施约束。
可以将范围适配器和管道操作的结果 (|
) 传递给需要函数对象的代码。 在下面的示例中,范围适配器创建的视图 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,则为引用rg
的ref_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
的专用化。 即:
- 如果
V
是empty_view
的专用化,或者是span
、basic_string_view
、iota_view
或者包含random_access_range
和sized_range
的subrange
的专用化,则结果是V
的专用化。 - 否则结果为
drop_view
。
注解
创建视图后,视图中的元素数保持不变,即使从更改创建它的视图也是如此。 但是,如果基础视图发生更改,则访问返回的视图中的元素可能会导致未定义的行为。
drop
与 take
相反。
前面显示为 "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_while
与 take_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
// 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
。
注解
为了提高效率,在 filter
和 transform
与管道 |
一起使用时,先执行 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
要从流中提取的元素的类型。
返回值
此范围适配器等效于 ranges::basic_istream_view<Val, typename U::char_type, typename U::traits_type>(str)
,其中 U
是 str
的类型。
示例: 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
,则会收到两个子范围:1
和 3
。
相关的适配器是 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
的专用化。 即:
- 如果
V
为reverse_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
要在视图中存储的元素的值。
返回值
包含 t
的 single_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
,则会收到两个子范围:1
和 3
。
相关的适配器是 lazy_split
。 split_view
与 lazy_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
的专用化。 具体而言:
- 如果
V
是empty_view
的专用化,或者是span
、basic_string_view
、iota_view
或者包含random_access_range
和sized_range
的subrange
的专用化,则结果是V
的专用化。 - 否则结果为
take_view
。
备注
如果指定要接受的元素数多于 rg
中的元素,则将接受所有元素。
take
与 drop
相反。
前面显示为 "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_while
与 drop_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
。
备注
为了提高效率,在 filter
和 transform
组合时,先执行 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;
}