自己的类支持基于范围的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.mzph.cn/news/105220.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 上传项目到 GitHub

一、前言 GitHub 作为时下最大的开源代码管理项目&#xff0c;广泛被工程和科研人员使用&#xff0c;本文主要介绍如何如何将自己的项目程序上传到 GitHub 上。 要上传本地项目到 GitHub 上&#xff0c;主要分为两步&#xff0c;第一步是 二、创建 SSH keys 首先登录 GitHu…

js的入口函数和作用

JavaScript的入口函数通常是整个应用程序或网页的起点。在浏览器环境中&#xff0c;最常见的入口函数是main函数&#xff0c;或者在更现代的JavaScript框架&#xff08;如React或Vue&#xff09;中&#xff0c;可能会有其他特定的入口函数。 以下是一个非常基础的JavaScript入…

Jenkins+Gitlab+Docker(Dockerfile)部署

Docker部署运行 ​ 上一篇内容中使用Jenkins(运行服务器)Gitlab(代码存储库)Webhook(网络钩子)的方式部署运行我们的项目。需要我们在服务器上做好很多相关的环境配置及依赖。 ​ 那么假如有这样一个场景&#xff1a;需要把不同技术栈的项目部署到同一台服务器上运行。比如PH…

如何开始使用 Kubernetes RBAC

基于角色的访问控制 (RBAC) 是一种用于定义用户帐户可以在 Kubernetes 集群中执行的操作的机制。启用 RBAC 可以降低与凭证盗窃和帐户接管相关的风险。向每个用户授予他们所需的最低权限集可以防止帐户拥有过多的特权。 大多数流行的 Kubernetes 发行版都从单个用户帐户开始,…

【MySQL × SpringBoot 突发奇想】全面实现流程 · 数据库导出Excel表格文件的接口

文章目录 【MySQL SpringBoot 小点子】全面实现流程 数据库导出Excel表格文件的接口1. 什么是VO&#xff08;View Object&#xff09;对象2. BeanCopyUtils进行两个对象的数据转移3. mapper层实现4. service层实现5. vo对象创建6. 保存路径配置7. controller层核心代码实现8.…

JavaFX: 使用本地openjfx包

JavaFX: 使用本地openjfx包 1、注释配置2、下载openjfx包3、导入openjfx的jar包 1、注释配置 build.gradle配置注释&#xff1a; 2、下载openjfx包 下载javaFx地址&#xff1a;https://gluonhq.com/products/javafx/ 3、导入openjfx的jar包

设计模式~状态模式(state)-23

目录 (1)优点&#xff1a; (2)缺点&#xff1a; (3)使用场景&#xff1a; (4)注意事项&#xff1a; (5)应用实例&#xff1a; 代码 在状态模式&#xff08;State Pattern&#xff09;中&#xff0c;类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状…

elasticsearch安装

安装elasticsearch 1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; docker network create es-net1.2.加载镜像 elasticsearch的镜像的tar包&#xff1a;点击下载 将其上传到虚拟机中…

电源集成INN3270C-H215-TL、INN3278C-H114-TL、INN3278C-H215-TL简化了反激式电源转换器的设计和制造。

一、概述 InnoSwitch™3-CP系列IC极大地简化了反激式电源转换器的设计和制造&#xff0c;特别是那些需要高效率和/或紧凑尺寸的产品。InnoSwitch3-CP系列将初级和次级控制器以及安全额定反馈集成到单个IC中。 InnoSwitch3-CP系列器件集成了多种保护功能&#xff0c;包括线路过…

Web前端-Vue2+Vue3基础入门到实战项目-Day4(组件的三大组成部分, 组件通信, 案例-组件版小黑记事本, 进阶语法)

Web前端-Vue2Vue3基础入门到实战项目-Day4 组件的三大组成部分(结构/样式/逻辑)scoped样式冲突data是一个函数 组件通信组件通信语法父传子子传父props详解什么是propsprops检验props与data的区别 非父子(扩展)事件总线 (event bus)provide - inject 案例 - 小黑记事本(组件版)…

gRPC之gRPC Gateway

1、gRPC Gateway etcd3 API全面升级为gRPC后&#xff0c;同时要提供REST API服务&#xff0c;维护两个版本的服务显然不太合理&#xff0c;所以 grpc-gateway 诞生了。通过protobuf的自定义option实现了一个网关&#xff0c;服务端同时开启gRPC和HTTP服务&#xff0c; HTTP服…

京东数据接口:京东数据分析怎么做?

电商运营中数据分析的重要性不言而喻&#xff0c;而想要做数据分析&#xff0c;就要先找到数据&#xff0c;利用数据接口我们能够更轻松的获得比较全面的数据。因此&#xff0c;目前不少品牌商家都选择使用一些数据接口来获取相关电商数据、以更好地做好数据分析。 鲸参谋电商…

2023年中国云计算软件市场规模、市场结构及市场份额情况分析[图]

云计算是分布式计算的一种&#xff0c;指的是通过网络“云”将巨大的数据计算处理程序分解成无数个小程序&#xff0c;然后&#xff0c;通过多部服务器组成的系统进行处理和分析这些小程序得到结果并返回给用户。云计算软件类型分为三类&#xff0c;即基础设施即服务、平台即服…

点云从入门到精通技术详解100篇-基于多尺度深度特征分析的点云模型法向估计(续)

目录 3.3 网络结构与损失函数设计 3.3.1 网络结构 3.3.2 损失函数设计

Python算法练习 10.14

leetcode 2095 删除链表的中间节点 给你一个链表的头节点 head 。删除 链表的 中间节点 &#xff0c;并返回修改后的链表的头节点 head 。 长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点&#xff08;下标从 0 开始&#xff09;&#xff0c;其中 ⌊x⌋ 表示小于或等于…

非类型模板参数+模板的特化

目录 一、非类型模板参数 二、模板的特化 &#xff08;一&#xff09;函数模板特化 &#xff08;二&#xff09;类模板举例 1. 全特化 2. 偏特化 一、非类型模板参数 模板参数分类&#xff1a;类型形参与非类型形参。类型形参即&#xff1a;出现在模板参数列表中&#x…

通过webpack创建并打包js库到npm仓库

1.创建项目并进行基本配置 webpack配置文件&#xff1a; webpack.build.js const path require(path);module.exports {mode:development,entry:./src/webpack-numbers.js,output: {filename: webpack-numbers.js,path: path.resolve(__dirname, dist),clean: true,},}; p…

使用docker搭建kafka集群、可视化操作台

单机搭建 1 拉取zookeeper镜像 docker pull wurstmeister/zookeeper 2 启动zookeeper容器 docker run -d --name zookeeper -p 2181:2181 -v /etc/localtime:/etc/localtime wurstmeister/zookeeper 3 拉取kafka镜像 docker pull wurstmeister/kafka 4 启动kafka镜像 docker…

【玩机】如何修改iPhone充电提示音!最详细简单保姆级教程~ 学费了可替换任意音频做你的专属充电提示音!——后厂村路灯

其实方法很简单&#xff0c;利用快捷指令&#xff0c;获得base64 位的音频文本&#xff0c;然后再充电时播放即可。 视频教程 【玩机】如何修改iPhone充电提示音&#xff01;最详细简单保姆级教程 具体操作如下&#xff1a; 1.首先&#xff0c;网上找到需要设定的音频&#xf…

一个单身狗 和 两个单身狗

一个单身狗 一个数组中只有一个数字是出现一次&#xff0c;其他所有数字都出现了两次。 编写一个程序找出这个只出现一次的数字。 方法1(异或) a ^ a 0a ^ 0 a^满足结合律 #include<stdio.h> int main() {int arr[] { 1,2,3,4,5,4,2,3,5};int i 0;int ret 0;in…