在C++20之前,想要让自定义容器支持范围-for(range‑for)循环,通常需要为容器提供begin()和end()函数,并保证返回的迭代器满足相应的概念。随着C++20标准的推出,std::ranges::range概念使得这个过程更为简洁,只需实现begin()、end()并满足可比较、可解引用等属性即可。下面通过一个完整的例子来演示如何实现一个简单的自定义容器,并让它在范围-for中直接使用。
#include <iostream>
#include <vector>
#include <ranges>
#include <iterator>
#include <concepts>
// -----------------------------------------------------------------------------
// 1. 设计自定义容器:SimpleStack
// -----------------------------------------------------------------------------
template <typename T>
class SimpleStack {
public:
// 构造函数
SimpleStack() = default;
// 典型的堆栈操作
void push(const T& value) { data_.push_back(value); }
void pop() { data_.pop_back(); }
const T& top() const { return data_.back(); }
// 通过内部vector提供的 begin()/end() 使容器可迭代
auto begin() { return data_.begin(); }
auto end() { return data_.end(); }
// 对 const 对象提供 const 版本
auto begin() const { return data_.cbegin(); }
auto end() const { return data_.cend(); }
private:
std::vector <T> data_;
};
// -----------------------------------------------------------------------------
// 2. 使用范围-for遍历 SimpleStack
// -----------------------------------------------------------------------------
int main() {
SimpleStack <int> stack;
// 填充堆栈
for (int i = 1; i <= 5; ++i)
stack.push(i);
// 直接使用范围-for,迭代器自动符合 std::ranges::range
std::cout << "Stack contents (top to bottom): ";
for (const auto& val : stack)
std::cout << val << ' ';
std::cout << '\n';
// 通过标准库算法进行操作
std::cout << "Sum of elements: " << std::accumulate(stack.begin(), stack.end(), 0) << '\n';
return 0;
}
关键点解析
-
容器实现
SimpleStack使用内部 `std::vector ` 存储元素,提供常见的 `push`、`pop`、`top` 接口。- 为了兼容范围-for,直接公开
begin()和end()。由于std::vector的迭代器已经符合所有必要的概念,SimpleStack自然也满足std::ranges::range。
-
const 正确性
- 对于
const对象,也需要提供begin() const、end() const。这样在const上下文中也能安全迭代。
- 对于
-
与标准算法配合
- 只要容器满足
std::ranges::range,就可以与std::accumulate、std::sort(需要随机访问)等算法无缝使用。 - 由于
SimpleStack只提供前向迭代器,不能直接用std::sort,但可以用std::copy、std::find_if等前向算法。
- 只要容器满足
-
C++20 范围概念
- 在 C++20 之前,范围-for 的实现依赖于
std::begin、std::end并手动检查类型。 - C++20 的
std::ranges::range概念让编译器在语义层面自动判断容器是否可迭代,从而使代码更简洁、可读性更高。
- 在 C++20 之前,范围-for 的实现依赖于
扩展思路
-
自定义迭代器
如果想让容器内部使用自定义迭代器(如双向、随机访问),只需在begin()、end()返回自己的迭代器即可。确保迭代器满足相应的迭代器概念即可。 -
反向迭代
可以提供rbegin()、rend(),并让容器同时满足std::ranges::reverse_range,使得范围-for 可以直接用for (auto& v : stack | std::views::reverse)。 -
视图与管道
结合std::views,可以在不复制数据的情况下对容器进行筛选、映射等操作,例如for (auto x : stack | std::views::filter([](int v){ return v%2==0; }))。
通过以上实现,任何符合 std::ranges::range 的自定义容器都能无缝使用范围-for,提高代码的可读性与可维护性。