您的位置:首页 > 编程语言 > C语言/C++

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)
for (
range_declaration
:

range_expression
)

loop_statement

attr不用管

重点说range_expression

它可以是:

(1)原生数组

(2)实现了begin,end成员函数或普通函数的对象(后面有讲具体)

(3) 花括号初始化列表

对于(2),其实说的不太清楚,正常来说都是配对的。要么begin,end都是成员函数,要么begin end都是普通函数。如果一个是成员函数,一个是非成员函数。。。我实在看不懂这波英文该如何理解。。。

原文是an object for which
begin
and
end
member 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的规定有了大致了解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐