C++17 如何实现 可用于 C++17版 基于范围的for循环 的类型
2017-11-15 21:55
369 查看
第一种方式: 实现成员函数 begin(),end()。
第二种方式:实现普通函数 begin(被循环对象), end(被循环对象)。并且要注意,类中不要有叫begin,end的成员,不管是变量,函数,还是枚举值什么的,哪怕private的也不行。
两种方式都有以下要求:
begin返回值应该是一个类似迭代器的类型或者指针。所谓类似迭代器的类型,是指,有opeartor++() 也就是前置自增那个, 有operator*()。
end返回值没有以上要求,并且不要求和begin返回值类型一样。(相对C++11放宽了要求)
但是begin和end需要能够进行 != 比较, 也就是 begin!=end。注意有顺序的,begin在前,end在后。
下面的啰嗦可以不用看
参考网页http://en.cppreference.com/w/cpp/language/range-for
还有中文版http://zh.cppreference.com/w/cpp/language/range-for
里面各种细节写的很全了,基本上看上面网页就够了。还想细致的,就自己翻标准文档吧。
我下面的话基本上也不用看。。
C++17的基于范围的for循环长这个样子 (C++20允许再加一个init语句,C++17已经允许if,switch等加上init了,但是for居然没有?)
attr(optional)
range_expression
loop_statement
attr不用管
重点说range_expression
它可以是:
(1)原生数组
(2)实现了begin,end成员函数或普通函数的对象(后面有讲具体)
(3) 花括号初始化列表
对于(2),其实说的不太清楚,正常来说都是配对的。要么begin,end都是成员函数,要么begin end都是普通函数。如果一个是成员函数,一个是非成员函数。。。我实在看不懂这波英文该如何理解。。。
原文是an object for which
这一点其实可以先放着,因为后面就会讲到其他的一些规定。到时候就知道了
一个相关话题,C++17允许结构化绑定了,也就是类似下面这样
这里不多说。
attr(optional)
range_expression
loop_statement
会被翻译成
range_declaration
loop_statement
注意这里__begin和__end类型可以不一样。__begin需要有operator++,operator*, 还需要__begin和__end能够 !=比较
除此之外没有限制了。相比C++11放宽了约束。
重点在begin_expr和end_expr到底是什么。
以下三项按顺序,如果满足就不再向下走。
(1)如果range_expression(也就是之前说的,可以是三种东西的那个),是一个原生数组
那么begin_expr就是__range,(可以理解为指向数组头元素的指针,虽然数组和指针之间的转换我一直很晕。。。但是这里无所谓,完全可以这样理解)
end_expr就是__range+数组元素个数。
如果数组元素个数不知道(也就是说,一个原生的动态分配的数组,表现为一个指针,不知道size。或者说一个静态数组,强行转换到指针,丢失了其size信息。等各种情况),或者说数组是不完全类型(这什么鬼?一个数组,其元素是不完全类型的意思?),那么程序不合法
(2)如果range_expression是一个类型。它有成员begin,或者有成员end,(或者都有)。那么begin_expr和end_expr扩展成 __range.begin() __range.end()
注意这里说的有成员begin或end,是不管其类型和访问权限的。 也就是说哪怕成员begin是一个变量,一个枚举值,也会适用这条。 所以这里就解决了之前的问题,如果begin和end,一个是成员函数,一个非成员。会导致统一用成员,然后有一个找不到
(3)如果走到了这一步,那么统一用非成员函数begin,end。 扩展成begin(__range)这样。(begin,end的查找规则写的术语我不认识。。)
根据这些规则。我们如果需要实现一个自己的 可以被范围for循环的类型, 需要:
(1)实现成员函数begin,end
(2)或者,实现普通函数 begin(被循环对象),end(被循环对象)。与此同时,我们的类型中,不能有叫begin或者end的东西。
并且,不管哪种方式。begin和end的返回值需要能够做到++begin *begin begin!=end这些操作。
(经实验,begin返回值还必须是一个指针或者类,比如说不能是一个int。。end返回值没有这个说法)
------------------------------------------------
之前我在我自己的文章 http://blog.csdn.net/zhangfengz1995/article/details/78123836
里面提到了C++17的基于范围的for循环的改变。
过去的基于范围for循环的缺点是, 要求begin和end的类型一样,都是iterator。这个约束超过了需要的程度,所以是过度约束。对于begin,的确需要是一个iterator,需要对它步进。但是对于end,实际上我们需要的只是它能和begin比较即可。
所以C++17修改了基于范围for循环的展开方式。现在begin和end不需要一样的类型。只需要能够比较即可。
(可以看http://blog.csdn.net/zwvista/article/details/52304605
和https://stackoverflow.com/questions/39117330/how-the-new-range-based-for-loop-in-c17-helps-ranges-ts)
但是真正使用中应该如何写一个自己的可以被基于范围for循环使用的东西呢。
一直以为只需要自己实现被迭代对象的成员函数,begin和end。begin返回的对象需要实现operator ++,operator* 。还需要实现begin对象和end对象的operator !=。
今天看其他的文章,看到了其他的写法。发现了一些以前忽略的细节
http://blog.csdn.net/zmdsjtu/article/details/77371428
这篇文章,用到了基于范围的for循环。但是被循环对象是一个directory_iterator,这个对于C++来说感觉有些奇怪。(对于python来说倒是挺常见的)
所以去翻了翻cpp reference。看到了这里的directory_iterator的定义
http://en.cppreference.com/w/cpp/filesystem/directory_iterator
我想象中,这个directory_iterator应该定义两个成员函数,一个begin,一个end。因为有了新语法,end的返回值不需要是一个迭代器。只需要begin和end能够比较即可(也就是可选择性的,再实现一个begin和end的operator !=)
但是这里的定义是,directory_iterator有两个相关的非成员函数,begin和 end
返回同样是directory_iterator
实现了directory_iterator之间的比较
奇怪的点在于,begin和end是非成员函数
begin,end函数原型是
directory_iterator begin( directory_iterator iter)noexcept;
directory_iterator end(const directory_iterator&)noexcept;
这就比较奇怪了。难道新的基于范围for循环展开的时候不是调用被循环对象的成员函数begin和end么。。。
莫非实现方式不仅允许成员函数,还可以用非成员函数。
(甚至说。。以后非成员函数才是正道。???。因为标准库都这样用了)
看了看http://en.cppreference.com/w/cpp/language/range-for
才对新标准的range-based for loop的规定有了大致了解。
第二种方式:实现普通函数 begin(被循环对象), end(被循环对象)。并且要注意,类中不要有叫begin,end的成员,不管是变量,函数,还是枚举值什么的,哪怕private的也不行。
两种方式都有以下要求:
begin返回值应该是一个类似迭代器的类型或者指针。所谓类似迭代器的类型,是指,有opeartor++() 也就是前置自增那个, 有operator*()。
end返回值没有以上要求,并且不要求和begin返回值类型一样。(相对C++11放宽了要求)
但是begin和end需要能够进行 != 比较, 也就是 begin!=end。注意有顺序的,begin在前,end在后。
下面的啰嗦可以不用看
参考网页http://en.cppreference.com/w/cpp/language/range-for
还有中文版http://zh.cppreference.com/w/cpp/language/range-for
里面各种细节写的很全了,基本上看上面网页就够了。还想细致的,就自己翻标准文档吧。
我下面的话基本上也不用看。。
C++17的基于范围的for循环长这个样子 (C++20允许再加一个init语句,C++17已经允许if,switch等加上init了,但是for居然没有?)
attr(optional)
for (range_declaration
:
range_expression
)
loop_statement
attr不用管
重点说range_expression
它可以是:
(1)原生数组
(2)实现了begin,end成员函数或普通函数的对象(后面有讲具体)
(3) 花括号初始化列表
对于(2),其实说的不太清楚,正常来说都是配对的。要么begin,end都是成员函数,要么begin end都是普通函数。如果一个是成员函数,一个是非成员函数。。。我实在看不懂这波英文该如何理解。。。
原文是an object for which
beginand
endmember functions or free functions are defined
这一点其实可以先放着,因为后面就会讲到其他的一些规定。到时候就知道了
一个相关话题,C++17允许结构化绑定了,也就是类似下面这样
for (auto&& [first,second] : mymap) { // use first and second }
这里不多说。
attr(optional)
for (range_declaration
:
range_expression
)
loop_statement
会被翻译成
{
auto && __range =range_expression
;
auto __begin =begin_expr
;
auto __end =end_expr
;
for ( ; __begin != __end; ++__begin) {
range_declaration
= *__begin;
loop_statement
}
}
注意这里__begin和__end类型可以不一样。__begin需要有operator++,operator*, 还需要__begin和__end能够 !=比较
除此之外没有限制了。相比C++11放宽了约束。
重点在begin_expr和end_expr到底是什么。
以下三项按顺序,如果满足就不再向下走。
(1)如果range_expression(也就是之前说的,可以是三种东西的那个),是一个原生数组
那么begin_expr就是__range,(可以理解为指向数组头元素的指针,虽然数组和指针之间的转换我一直很晕。。。但是这里无所谓,完全可以这样理解)
end_expr就是__range+数组元素个数。
如果数组元素个数不知道(也就是说,一个原生的动态分配的数组,表现为一个指针,不知道size。或者说一个静态数组,强行转换到指针,丢失了其size信息。等各种情况),或者说数组是不完全类型(这什么鬼?一个数组,其元素是不完全类型的意思?),那么程序不合法
(2)如果range_expression是一个类型。它有成员begin,或者有成员end,(或者都有)。那么begin_expr和end_expr扩展成 __range.begin() __range.end()
注意这里说的有成员begin或end,是不管其类型和访问权限的。 也就是说哪怕成员begin是一个变量,一个枚举值,也会适用这条。 所以这里就解决了之前的问题,如果begin和end,一个是成员函数,一个非成员。会导致统一用成员,然后有一个找不到
(3)如果走到了这一步,那么统一用非成员函数begin,end。 扩展成begin(__range)这样。(begin,end的查找规则写的术语我不认识。。)
根据这些规则。我们如果需要实现一个自己的 可以被范围for循环的类型, 需要:
(1)实现成员函数begin,end
(2)或者,实现普通函数 begin(被循环对象),end(被循环对象)。与此同时,我们的类型中,不能有叫begin或者end的东西。
并且,不管哪种方式。begin和end的返回值需要能够做到++begin *begin begin!=end这些操作。
(经实验,begin返回值还必须是一个指针或者类,比如说不能是一个int。。end返回值没有这个说法)
------------------------------------------------
之前我在我自己的文章 http://blog.csdn.net/zhangfengz1995/article/details/78123836
里面提到了C++17的基于范围的for循环的改变。
过去的基于范围for循环的缺点是, 要求begin和end的类型一样,都是iterator。这个约束超过了需要的程度,所以是过度约束。对于begin,的确需要是一个iterator,需要对它步进。但是对于end,实际上我们需要的只是它能和begin比较即可。
所以C++17修改了基于范围for循环的展开方式。现在begin和end不需要一样的类型。只需要能够比较即可。
(可以看http://blog.csdn.net/zwvista/article/details/52304605
和https://stackoverflow.com/questions/39117330/how-the-new-range-based-for-loop-in-c17-helps-ranges-ts)
但是真正使用中应该如何写一个自己的可以被基于范围for循环使用的东西呢。
一直以为只需要自己实现被迭代对象的成员函数,begin和end。begin返回的对象需要实现operator ++,operator* 。还需要实现begin对象和end对象的operator !=。
今天看其他的文章,看到了其他的写法。发现了一些以前忽略的细节
http://blog.csdn.net/zmdsjtu/article/details/77371428
这篇文章,用到了基于范围的for循环。但是被循环对象是一个directory_iterator,这个对于C++来说感觉有些奇怪。(对于python来说倒是挺常见的)
所以去翻了翻cpp reference。看到了这里的directory_iterator的定义
http://en.cppreference.com/w/cpp/filesystem/directory_iterator
我想象中,这个directory_iterator应该定义两个成员函数,一个begin,一个end。因为有了新语法,end的返回值不需要是一个迭代器。只需要begin和end能够比较即可(也就是可选择性的,再实现一个begin和end的operator !=)
但是这里的定义是,directory_iterator有两个相关的非成员函数,begin和 end
返回同样是directory_iterator
实现了directory_iterator之间的比较
奇怪的点在于,begin和end是非成员函数
begin,end函数原型是
directory_iterator begin( directory_iterator iter)noexcept;
directory_iterator end(const directory_iterator&)noexcept;
这就比较奇怪了。难道新的基于范围for循环展开的时候不是调用被循环对象的成员函数begin和end么。。。
莫非实现方式不仅允许成员函数,还可以用非成员函数。
(甚至说。。以后非成员函数才是正道。???。因为标准库都这样用了)
看了看http://en.cppreference.com/w/cpp/language/range-for
才对新标准的range-based for loop的规定有了大致了解。
相关文章推荐
- 【C++ STL应用与实现】26: 如何使用std::for_each以及基于范围的for循环 (since C++11)
- Android基于mAppWidget实现手绘地图(十三)–如何显示/隐藏任意类型的地图对象
- 仅返回类型不同的函数,在C++中如何实现重载?
- Modern C++(二)Range-based for loop(基于范围的for循环)
- Swift编程语言中如何实现自定义类型的for-in循环(基于Swift 2.2)
- 基于 wke 的浏览器:如何实现 js 和 c++ 的互相调用
- C++ 11 学习2:空指针(nullptr) 和 基于范围的for循环(Range-based for loops)
- 基于DevExpress开发的GridView如何实现一列显示不同的控件类型
- C++如何实现任意类型的数据交换
- 【C++学习笔记】基于范围的for循环(C++11)
- 在c++中如何实现非consle类型的计时器
- C++中如何实现自定义类型的迭代器
- 基于流加密(stream cipher)下如何依靠多段密文获取特定密文的明文(c++实现)
- 【C++】学习笔记十七——基于范围的for循环
- C或C++如何实现基于CAN的SAE J1939协议??
- 仅返回类型不同的函数,在C++中如何实现重载?
- C++如何通过ostringstream实现任意类型转string
- C++中基本数据类型字节数及取值范围,下面有实现查看代码和运行截图
- Windows Phone 8 学习志(探索问题一:如何简单利用Windows Phone Runtime Component项目类型实现C#和C++交互)
- C++ 11 学习2:空指针(nullptr) 和 基于范围的for循环(Range-based for loops)