它提供了对序列(如数组、容器等)的非拥有性、只读或可写的访问。(就像是个透明的放大镜,它能让你去看一组数据,但它自己不拥有这些数据。)
#include
#include
#include
int main() {
std::vector numbers = {1, 2, 3, 4, 5};
// 创建一个视图,它是对numbers向量的一个抽象表示
auto view = std::views::all(numbers);
for (auto num : view) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在这个例子中,std::views::all
创建了一个视图,它包含了 numbers
向量中的所有元素。通过视图可以遍历这些元素,而不需要复制数据。
std::views::transform
是干啥的?td::views::transform
是个很有用的工具,它能对一组数据里的每个元素做转换。你可以把它想象成一个小魔法师,能把每个元素变成你想要的样子
transform
或filter
)在遍历时才会执行。auto square = [](int x) {
std::cout << "Processing " << x << std::endl;
return x * x;
};
auto view = data | std::views::transform(square);
// 此时未调用square,直到遍历view时才输出日志
“|
” 是范围(ranges)库中的管道运算符,它能够把一个范围(如 std::vector
)和视图适配器进行连接。
auto squared = data | std::views::transform([](int x) { return x * x; });
std::views::transform
是一个视图适配器,其作用是对范围中的每个元素执行转换操作。它接收一个可调用对象(通常是 lambda 表达式)作为参数,这个可调用对象会对每个元素进行处理。[](int x) { return x * x; }
是一个 lambda 表达式,它接收一个 int
类型的参数 x
,并返回 x
的平方。auto squared
借助 auto
关键字自动推导 squared
的类型。squared
实际上是一个视图对象,它代表了对 data
中每个元素进行平方操作后的结果。需要注意的是,这里并没有立即计算平方值,而是在遍历时才会进行计算,这就是延迟计算。std::vector data{1, 2, 3, 4, 5};
auto result = data
| std::views::filter([](int x) { return x % 2 == 0; }) // 过滤偶数
| std::views::transform([](int x) { return x * 2; }); // 每个数乘2
// 遍历时才执行:先过滤出2,4 → 再转换为4,8
filter + transform
)。std::for_each
:立即执行,需显式处理每个元素,无法组合操作。// 视图:延迟计算,链式操作
auto result = data | std::views::filter(is_even) | std::views::transform(square);
// std::for_each:立即执行,需手动处理
std::for_each(data.begin(), data.end(), [](int x) { /* 处理逻辑 */ });
#include
#include
#include
int main() {
std::vector numbers = {1, 2, 3, 4, 5};
// 过滤出偶数
auto even_view = numbers | std::views::filter([](int x) { return x % 2 == 0; });
// 将过滤后的偶数乘以2
auto transformed_view = even_view | std::views::transform([](int x) { return x * 2; });
for (auto num : transformed_view) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
std::views::filter
视图适配器过滤出 numbers
向量中的偶数std::views::transform
视图适配器将过滤后的偶数乘以 2实现机制:视图通过封装迭代器和函数对象,在遍历时动态应用操作(如transform_view
存储转换函数,filter_view
存储谓词)
break
时跳过后续计算)transform
函数被多次调用),可通过std::ranges::to
缓存结果优化若底层容器被修改、发生插入/删除(如vector
扩容导致迭代器失效),视图可能引用无效内存。(图通过封装迭代器和函数对象)
最佳实践:
std::list
或预留容量)std::vector data = {1, 2, 3};
data | std::views::transform([](int& x) { x *= 2; }); // 原地修改
自定义视图适配器时需要注意以下几点:
std::ranges::view_interface
类,以确保视图符合视图的接口要求。iterator
、sentinel
等。#include
#include
#include
// 自己做的视图工具
template
class custom_view : public std::ranges::view_interface> {
private:
R base_;
public:
custom_view(R r) : base_(std::move(r)) {}
auto begin() { return std::ranges::begin(base_); }
auto end() { return std::ranges::end(base_); }
};
// 做视图工具的工厂函数
template
auto make_custom_view(R&& r) {
return custom_view>(std::forward(r));
}
int main() {
std::vector numbers = {1, 2, 3, 4, 5};
auto view = make_custom_view(numbers);
for (auto num : view) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
定义了一个自己的视图工具 custom_view
,它继承了 std::ranges::view_interface
。写了 begin
和 end
方法,这样就能一个一个地去看数据了。还写了一个工厂函数 make_custom_view
来创建这个视图工具。最后用这个工具去看 numbers
里的数字,并且把它们都输出出来。
template
class StrideView : public std::ranges::view_interface> {
V base_;
size_t stride_;
public:
StrideView(V base, size_t stride) : base_(base), stride_(stride) {}
auto begin() {
return std::ranges::begin(base_); // 实际需自定义迭代器步进逻辑
}
auto end() { return std::ranges::end(base_); }
};
// 使用示例:每隔2个元素取一个
auto view = data | StrideView(2);
view_interface
,定义迭代器步进逻辑。begin()
和end()
,控制迭代器步长。场景:生成无限序列(如传感器数据),按需消费。内存友好,避免缓存全部数据。
generator infinite_stream() {
for (int i : std::views::iota(0) | std::views::transform(process_data)) {
co_yield i; // 每次协程恢复时生成一个处理后的数据
}
}
template
class thread_safe_view : public std::ranges::view_interface> {
std::mutex mtx;
T& data;
public:
auto begin() {
std::lock_guard lock(mtx);
return std::begin(data);
}
// 类似实现 end()
};
filter
视图动态筛选关键日志。transform
视图批量计算粒子效果位置。