语言特性 -- 自定义类型实现范围for循环

芝兰生于深谷,不以无人而不芳
君子修身养德,不以穷困而改志

标准库容器范围for

  • vector
std::vector<int>  vec{1, 1, 2, 3, 5, 8, 13};

for (auto v : vec) {
  std::cout << v << " ";
}
// output: 1 1 2 3 5 8 13
  • map
std::map<int, bool> items{{1, true}, {2, false}, {3, true}};

for (auto item : items) {
  std::cout << "(" << item.first << ", " << item.second << ") ";
}
// output: (1, 1) (2, 0) (3, 1)

c++17可以使用结构化绑定的方式

for (auto [key, value] : items) {
  std::cout << "(" << key << ", " << value << ") ";
}
// output: (1, 1) (2, 0) (3, 1)

自定义类型

为了展示如何为自定义类型可以使用范围for循环,我们以下面的简单数组的实现为例说明

template<typename T, size_t const Size>
struct dummy_array {
  T const& get_at(size_t const index) const {
    if (index < Size) return data[index];
    throw std::out_of_range("index out of range");
  }

  void set_at(size_t const index, T const& value) {
    if (index < Size) data[index] = value;
    else throw std::out_of_range("index out of range");
  }

  size_t size() const { return Size; }

private:
  T data[Size] = {};
};

我们需要如何让下面的代码通过

dummy_array<int, 3> arr;
arr.set_at(0, 1);
arr.set_at(1, 2);
arr.set_at(2, 3);

for (auto&& e : arr) {  // 编译错误
  std::cout << e << '\n';
}

How to do it

为了完成上述的操作,我们需要完成两件事情

创建可变和常量迭代器

operator++(包括前缀和后缀版本)用于递增迭代器
operator* 用于解引用迭代器并访问迭代器指向的实际元素
operator!= 用于与另一个迭代器进行比较

以上面的dummy_array为例,我们先实现它的迭代器

template<typename T, typename C, size_t const Size>
struct dummy_array_iterator_type {
  dummy_array_iterator_type(C& collection, size_t const index)
      : index(index), collection(collection) {}

  bool operator!=(dummy_array_iterator_type const& other) const {
    return index != other.index;
  }

  T const& operator*() const {
    return collection.GetAt(index);
  }

  dummy_array_iterator_type& operator++() {
    ++index;
    return *this;
  }

  dummy_array_iterator_type operator++(int) {
    auto temp = *this;
    ++*temp;
    return temp;
  }

private:
  size_t index;
  C& collection;
};

声明别名模板,mutableconst类型都需要声明

template<typename T, size_t const Size>
using dummy_array_iterator = dummy_array_iterator_type<T, dummy_array<T, Size>, Size>;

template<typename T, size_t const Size>
using dummy_array_const_iterator = dummy_array_iterator_type<T, dummy_array<T, Size> const, Size>;

提供begin() 和 end() 的自由函数

begin() 和 end() 函数返回相应的开始和结束迭代器,上面两个别名模板都需要有重载:

template<typename T, size_t const Size>
inline dummy_array_iterator<T, Size> begin(dummy_array<T, Size>& collection) {
  return dummy_array_iterator<T, Size>(collection, 0);
}

template<typename T, size_t const Size>
inline dummy_array_iterator<T, Size> end(dummy_array<T, Size>& collection) {
  return dummy_array_iterator<T, Size>(collection, collection.GetSize());
}

template<typename T, size_t const Size>
inline dummy_array_const_iterator<T, Size> begin(dummy_array<T, Size> const& collection) {
  return dummy_array_const_iterator<T, Size>(collection, 0);
}

template<typename T, size_t const Size>
inline dummy_array_const_iterator<T, Size> end(dummy_array<T, Size> const& collection) {
  return dummy_array_const_iterator<T, Size>(collection, collection.GetSize());
}

begin() 和 end() 函数对于这两种类型的迭代器都有两个重载,是为了常量和非常量实例的都可以使用范围 for 循环。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容