1. 背景与动机
在 C++20 之前,集合操作(如 filter, map, sort 等)往往需要显式地写出迭代器、算法和临时容器。虽然 `
` 提供了强大的函数式接口,但缺乏直观的管道式链式调用语法,导致代码冗长且可读性不佳。C++23 在 “ 头文件中引入了 **管道运算符 `|`** 以及 **视图(view)**,使得我们可以用更接近自然语言的方式组合算法,极大提升代码的可读性与维护性。
### 2. 核心概念
– **视图(View)**:只读的、懒执行的、对底层序列进行变换的轻量对象。它不存储数据,而是保持对原始容器的引用或迭代器。
– **管道运算符 `|`**:把左侧的可迭代对象传递给右侧的视图或算法,形成链式调用。
– **变换函数(Transformations)**:`std::views::filter`, `std::views::transform`, `std::views::take`, `std::views::reverse` 等。
– **消耗器(Consumers)**:如 `std::ranges::for_each`, `std::ranges::accumulate`, `std::ranges::sort` 等。
### 3. 基础示例
“`cpp
#include
#include
#include
#include
int main() {
std::vector
data = {1, 2, 3, 4, 5, 6};
// 先过滤偶数,再平方,最后求和
int sum = data
| std::views::filter([](int x){ return x % 2 == 0; })
| std::views::transform([](int x){ return x * x; })
| std::ranges::fold(0, std::plus());
std::cout 说明:
> – `std::views::filter` 只保留满足谓词的元素。
> – `std::views::transform` 对每个元素执行 lambda。
> – `std::ranges::fold` 是对视图进行累积(C++23 新增的消费者),等价于 `std::accumulate`。
### 4. 进阶使用:组合自定义视图
有时标准视图不够用,可以自定义一个**投影视图**。下面实现一个 `std::views::select`,用于从 `std::pair` 或结构体中提取成员。
“`cpp
#include
#include
#include
#include
namespace my_views {
template
struct select_view : std::ranges::view_base {
using iterator_category = std::forward_iterator_tag;
using value_type = std::invoke_result_t>::iterator>())>>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
select_view(std::ranges::subrange>::iterator> rng, MemberPtr mp)
: rng_(rng), mp_(mp) {}
struct iter {
using iterator_type = std::vector>::iterator;
iterator_type current_;
MemberPtr mp_;
using iterator_category = std::forward_iterator_tag;
using value_type = std::invoke_result_t>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
iter(iterator_type it, MemberPtr mp) : current_(it), mp_(mp) {}
reference operator*() const { return (*current_).*mp_; }
iter& operator++() { ++current_; return *this; }
bool operator==(const iter& other) const { return current_ == other.current_; }
};
iter begin() const { return iter(rng_.begin(), mp_); }
iter end() const { return iter(rng_.end(), mp_); }
private:
std::ranges::subrange>::iterator> rng_;
MemberPtr mp_;
};
template
select_view> select(std::ranges::subrange>::iterator> rng, MemberPtr mp) {
return select_view>(rng, mp);
}
}
int main() {
std::vector> v{{1,”a”},{2,”b”},{3,”c”}};
auto names = v
| std::views::transform([](auto &p){ return &p.second; }) // 取地址
| my_views::select(v, &std::pair::second); // 提取成员
for (auto name : names)
std::cout 注意:上例中自定义视图相对复杂,实际开发中通常使用 `std::views::transform` 或 `std::views::elements`(C++23)即可完成成员提取。
### 5. 性能与懒执行
– **懒执行**:视图链式调用不会立即产生中间容器,只有在消费者触发遍历时才会执行。
– **避免拷贝**:只对需要的元素进行计算,减少不必要的数据复制。
– **单次遍历**:多个 `filter`、`transform` 组合在一次遍历中完成,提升 cache 命中率。
### 6. 常见误区
1. **误以为 `std::ranges::for_each` 需要容器**
`for_each` 接受的是可迭代对象,视图同样适用。
2. **忘记包含 `
`**
`
` 在 C++20 起已经存在,C++23 提供了更多消费者。
3. **对视图的生命周期管理不当**
视图内部持有对原始容器的引用,若容器已析构,视图将失效。
4. **使用 `std::views::common` 时忘记兼容不同视图**
`common` 将非常规范围转换为普通范围,便于使用范围 for 循环。
### 7. 结语
C++23 的 `
` 与管道式编程为我们提供了更优雅、更直观的集合操作方式。它将算法、视图与消费者解耦,利用懒执行与链式调用实现了高效、可读性强的代码。掌握这些技巧后,日常的 STL 使用将更像写自然语言,而不再是堆砌函数模板。祝你编码愉快!