std::ranges::views::take, take_while,std::ranges::take_view,take_while_view
std::ranges::views::take and std::ranges::take_view
是C++20 引入的范围库中的组件,用于创建包含原范围前 N 个元素的视图。
核心概念
-
std::ranges::take_view
:-
一个范围适配器,生成原范围的前 N 个元素的视图。
-
若原范围元素少于 N 个,则取整个范围。
-
惰性求值:不会立即复制元素,只在遍历时生成。
-
满足
view
概念(拷贝/移动成本低)。
-
-
std::ranges::views::take
:-
范围适配器闭包对象(Range Adaptor Closure Object),可通过管道操作符
|
链式调用。 -
生成
take_view
,语法更简洁。
-
使用方式
1. 直接构造 take_view
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// 创建包含前 3 个元素的视图
auto tv = std::ranges::take_view(v, 3);
for (int x : tv) {
std::cout << x << " "; // 输出: 1 2 3
}
}
2. 通过 views::take
适配器
#include <ranges>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
auto tv = v | std::views::take(3); // 链式调用
for (int x : tv) {
std::cout << x << " "; // 输出: 1 2 3
}
}
典型场景示例
示例 1:处理有限范围
#include <algorithm>
#include <iostream>
#include <ranges>
int main()
{
namespace views = std::views;
auto print = [](char x){ std::cout << x; };
for (const char nums[]{'1', '2', '3'};
int n : views::iota(0, 5))
{
std::cout << "take(" << n << "): ";
// safely takes only upto min(n, nums.size()) elements:
std::ranges::for_each(nums | views::take(n), print);
std::cout << '\n';
}
}
Output:
take(0): take(1): 1 take(2): 12 take(3): 123 take(4): 123
示例 2:处理无限序列
#include <ranges>
#include <iostream>
int main() {
// 生成自然数序列 0,1,2,...,取前 5 个
auto infinite = std::views::iota(0) | std::views::take(5);
for (int x : infinite) {
std::cout << x << " "; // 输出: 0 1 2 3 4
}
}
示例 3:链式组合适配器
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {5, 2, 8, 3, 1, 4};
// 过滤偶数后取前 2 个
auto result = v
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::take(2);
for (int x : result) {
std::cout << x << " "; // 输出: 2 8
}
}
注意事项
-
N 为非负整数:传入负数可能导致未定义行为。
-
性能优势:惰性求值避免复制大范围。
-
兼容性:可与任何满足
std::ranges::range
的容器或视图组合。
通过 take_view
和 views::take
,可以高效处理范围的前 N 个元素,是函数式编程风格的典型工具。
std::ranges::views::take_while and std::ranges::take_while_view
C++20 范围库中用于根据条件截取范围元素的组件。它们会生成一个视图,包含原范围中从起始位置到第一个不满足条件的元素之前的所有元素。
核心概念
-
std::ranges::take_while_view
:-
根据谓词(Predicate)截取原范围中满足条件的元素,直到遇到第一个不满足条件的元素为止。
-
若所有元素都满足条件,则包含整个范围。
-
惰性求值:仅在遍历时检查条件,不提前计算。
-
满足
view
概念(低开销拷贝/移动)。
-
-
std::ranges::views::take_while
:-
范围适配器闭包对象(Range Adaptor Closure Object),可通过管道操作符
|
链式调用。 -
生成
take_while_view
,语法更简洁。
-
使用方式
1. 直接构造 take_while_view
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {2, 4, 6, 7, 8, 10};
// 截取元素,直到遇到第一个奇数(7)
auto tv = std::ranges::take_while_view(v, [](int x) { return x % 2 == 0; });
for (int x : tv) {
std::cout << x << " "; // 输出: 2 4 6
}
}
2. 通过 views::take_while
适配器
#include <ranges>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {5, 3, 8, 2, 1, 4};
// 修复:将 prev 初始化为首个元素的值
// 使用引用捕获外部变量(或直接重构逻辑)
int prev = v.front(); // 假设 v 非空
auto tv = v | std::views::take_while(
[&prev](int x) { // 引用捕获 prev
bool result = (x >= prev);
prev = x;
return result;
}
);
for (int x : tv) {
std::cout << x << " "; //输出 5
}
}
典型场景示例
示例 1:
#include <iostream>
#include <ranges>
int main()
{
for (int year : std::views::iota(2020)
| std::views::take_while([](int y){ return y < 2026; }))
std::cout << year << ' ';
std::cout << '\n';
const char note[]{"Today is yesterday's tomorrow!..."};
auto not_dot = [](char c){ return c != '.'; };
for (char x : std::ranges::take_while_view(note, not_dot))
std::cout << x;
std::cout << '\n';
}
Output:
2020 2021 2022 2023 2024 2025 Today is yesterday's tomorrow!
示例 2:处理无限序列
#include <ranges>
#include <iostream>
int main() {
// 生成自然数序列 0,1,2,...,截取直到数值 >=5
auto infinite = std::views::iota(0)
| std::views::take_while([](int x) { return x < 5; });
for (int x : infinite) {
std::cout << x << " "; // 输出: 0 1 2 3 4
}
}
示例 3:链式组合适配器
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {5, 8, 2, 3, 1, 4};
// 1. 先过滤偶数,再截取直到数值 <5
auto result = v
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::take_while([](int x) { return x >= 5; });
for (int x : result) {
std::cout << x << " "; // 输出: 8(过滤后为 [8,2,4],但 8 >= 5,2 < 5)
// 实际输出为 8
// 更正:filter 后的序列是 [2,8,4],take_while(x>=5) 会检查每个元素:
//第一个元素 8>=5成立,第二个元素 2 >=5 不成立,则停止,因此结果为8。
}
}
注意事项
-
谓词要求:
-
谓词必须接受范围的元素类型参数并返回
bool
。 -
若谓词有状态(如示例 2 中的
prev
),需标记为mutable
。
-
-
提前终止:
-
一旦遇到不满足条件的元素,立即停止遍历,后续元素不再检查。
-
与
std::views::filter
不同,take_while
不保留后续满足条件的元素。
-
-
兼容性:
-
适用于任何满足
std::ranges::input_range
的范围。 -
若范围在遍历过程中被修改,可能导致未定义行为。
-
-
性能:
-
惰性求值避免不必要的计算,但频繁调用谓词可能影响性能。
-