**使用C++20范围-for实现自定义容器的迭代器**

在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;
}

关键点解析

  1. 容器实现

    • SimpleStack 使用内部 `std::vector ` 存储元素,提供常见的 `push`、`pop`、`top` 接口。
    • 为了兼容范围-for,直接公开 begin()end()。由于 std::vector 的迭代器已经符合所有必要的概念,SimpleStack 自然也满足 std::ranges::range
  2. const 正确性

    • 对于 const 对象,也需要提供 begin() constend() const。这样在 const 上下文中也能安全迭代。
  3. 与标准算法配合

    • 只要容器满足 std::ranges::range,就可以与 std::accumulatestd::sort(需要随机访问)等算法无缝使用。
    • 由于 SimpleStack 只提供前向迭代器,不能直接用 std::sort,但可以用 std::copystd::find_if 等前向算法。
  4. C++20 范围概念

    • 在 C++20 之前,范围-for 的实现依赖于 std::beginstd::end 并手动检查类型。
    • C++20 的 std::ranges::range 概念让编译器在语义层面自动判断容器是否可迭代,从而使代码更简洁、可读性更高。

扩展思路

  • 自定义迭代器
    如果想让容器内部使用自定义迭代器(如双向、随机访问),只需在 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,提高代码的可读性与可维护性。

发表评论