当前位置: 首页 > news >正文

自己的类支持基于范围的for循环 (深入探索)

自己的类支持基于范围的for循环 (深入探索)

编译器实际运行伪代码为:

auto && __range = range_expression;
auto __begin = begin_expr;
auto __end = end_expr;
for (; __begin != __end; ++__begin) {range_declaration = *__begin;loop_statement
}

观察伪代码,关于迭代器用到的运算符,用到了 * , != 和 前置++

所以我们需要对迭代器实现以下的内容

  1. *
  2. !=
  3. ++ (prefix++)

然后根据c++标准(编译器)规定,我们还需要对对应的类实现begin和end函数,返回对应的迭代器

实现

迭代器类的实现
template <typename _Iterator>
class MyIterator {protected:_Iterator _M_current;typedef std::iterator_traits<_Iterator> __traits_type;// just use std::iterator_traits to trait the typespublic:typedef _Iterator iterator_type;typedef typename __traits_type::iterator_category iterator_category;typedef typename __traits_type::value_type value_type;typedef typename __traits_type::difference_type difference_type;typedef typename __traits_type::reference reference;typedef typename __traits_type::pointer pointer;public:MyIterator() noexcept : _M_current(_Iterator()) {}explicit MyIterator(const _Iterator& __i) noexcept : _M_current(__i) {}reference operator*() const noexcept { return *_M_current; }MyIterator& operator++() noexcept {++_M_current;return *this;}// not need base function. Just to expose _M_currentconst _Iterator& base() const noexcept { return _M_current; }
};// deal with different types of left and right != operators
template <typename _IteratorL, typename _IteratorR>
[[__nodiscard__]] inline bool operator!=(const MyIterator<_IteratorL>& __lhs,const MyIterator<_IteratorR>& __rhs) noexcept {return __lhs.base() != __rhs.base();
}// deal with same types of left and right != operators
template <typename _Iterator>
[[__nodiscard__]] inline bool operator!=(const MyIterator<_Iterator>& __lhs,const MyIterator<_Iterator>& __rhs) noexcept {return __lhs.base() != __rhs.base();
}

需要💡几点:

  • 这里的const都不是乱加的,都是有讲究的
  • 这里用到了标准库的迭代器类型萃取,没啥难度,但是对应特化版本写起来很麻烦,我这就不重新写了,理解上也很简单
  • 这里写了一个base函数,不是强制要求的,在这里也只是为了暴露指针,让全局的重载!=可以访问到,当然把指针设成public也可以
  • 重载的!=有两个版本,分别对应!=左右类型是否一致的情况

类的begin和end实现

实际上,我们只需要把下面这段代码插入到我们要的类里就可以了


/*
* And the data class must impl begin and end function
* to return the correspond iterator
* */
using iterator = MyIterator<T*>;
using const_iterator = MyIterator<const T*>;public:
iterator begin() { return iterator(str); }
iterator end() { return iterator(str + N); }
const_iterator begin() const { return const_iterator(str); }
const_iterator end() const { return const_iterator(str + N); }

这里的str和N换成你对应要的内容

当然这里只针对顺序容器,如果是其它类型,会复杂一点,迭代器也需要重新设计,就需要第二个参数了

标准库中普通顺序容器模板也是有两个,但是因为顺序可以直接对指针进行++,第二个用处不大,我这里也就直接省略了

示例

我这里给了一个最基本的使用demo示例,仅供参考:

template <typename T, size_t N>
class Test {public:Test() = default;Test(std::initializer_list<T> obj) {T* cur = str;for (const auto& val : obj) {*cur = val;++cur;}}T* data() {return str;}const T* data() const { return str; }/** And the data class must impl begin and end function* to return the correspond iterator* */using iterator = MyIterator<T*>;using const_iterator = MyIterator<const T*>;public:iterator begin() { return iterator(str); }iterator end() { return iterator(str + N); }const_iterator begin() const { return const_iterator(str); }const_iterator end() const { return const_iterator(str + N); }private:T str[N]{};public:friend std::ostream& operator<<(std::ostream& os, const Test& obj) {for (const auto& val : obj) {os << val << " ";}return os;}
};

使用示例:

  {Test<int, 10> test;for (auto& val : test) {static int i = 0;val = ++i;}std::cout << test << std::endl;}{const Test<int, 10> test{12, 34, 56, 78, 910};for (const auto& val : test) {std::cout << val << " ";}std::cout << std::endl;}

输出:

12 45 123 12 0 0 0 0 0 0  
1 2 3 4 5 6 7 8 9 10 
12 34 56 78 910 0 0 0 0 0 

小问题:如果一个类代码已经写好了,我们没法插入自己的begin函数和end函数怎么办

那么c++编译器在看你这个类如果没有begin和end函数,就回去查找全局的begin和end

那么全局的begin和end应该怎么写呢?

我是这么实现的,也顺利通过了,左右值/const也都走了对应版本

template <typename T, size_t N>
auto begin(Test<T, N>& a) {return MyIterator<T*>(a.data());
}
template <typename T, size_t N>
auto end(Test<T, N>& a) {return MyIterator<T*>(a.data() + N);
}
template <typename T, size_t N>
auto begin(const Test<T, N>& a) {return MyIterator<const T*>(a.data());
}
template <typename T, size_t N>
auto end(const Test<T, N>& a) {return MyIterator<const T*>(a.data() + N);
}

但怎么说呢,我觉得应该有更优雅的写法或者规范,但是这一块没有参考

不像前面写迭代器可以看标准库,保证不出错,但是这个标准库里肯定也没有示例,就不好说了

如果有谁看到有官方示例或推荐,欢迎来联系我

http://www.lryc.cn/news/192999.html

相关文章:

  • Multi Scale Supervised 3D U-Net for Kidney and Tumor Segmentation
  • 《操作系统真象还原》第一章 部署工作环境
  • SpringCloud-Config
  • 劣币驱良币的 pacing 之殇
  • Gin 中的 Session(会话控制)
  • ChatGPT AIGC 实现数据分析可视化三维空间展示效果
  • Stable Diffusion 动画animatediff-cli-prompt-travel
  • fatal error C1083: 无法打开包括文件: “ta_libc.h”: No such file or directory
  • c 语言基础题目:L1-034 点赞
  • SaaS人力资源管理系统的Bug
  • GPTQ 和 AWQ:LLM 量化方法的比较
  • JVM:虚拟机类加载机制
  • PHP筆記
  • IDEA启动报错Failed to create JVM. JVM path的解决办法
  • 源码解析FlinkKafkaConsumer支持周期性水位线发送
  • Nginx:动静分离(示意图+配置讲解)
  • 通讯网关软件024——利用CommGate X2Access实现Modbus TCP数据转储Access
  • vim工具的使用
  • Docker学习_存储篇
  • 微信小程序获取当前日期时间
  • Unity关键词语音识别
  • SpringBoot的配置文件——.yml和.properties
  • Retrieve Anything To Augment Large Language Models
  • 什么是面向对象编程
  • c++视觉处理----固定阈值操作:Threshold()函数,实时处理:二值化,反二值化,截断,设为零,反向设为零
  • KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(8)
  • 【问题解决】Ubuntu 安装 SeisSol 依赖 easi 报错解决: undefined reference to `H5free_memory‘
  • 循环小数(Repeating Decimals, ACM/ICPC World Finals 1990, UVa202)rust解法
  • [GAMES101]透视投影变换矩阵中为什么需要改变z值
  • sklearn处理离散变量的问题——以决策树为例