c++ foreach宏

来源:互联网 发布:c51单片机中断 编辑:程序博客网 时间:2024/06/04 23:32

现在很多语言都支持foreach关键字,虽然看起来完全可以用for来实现完全一样的功能,但是foreach使用起来却更方便。

下面是C++中的一个循环(很多情况下,我们都是在一个迭代器范围内进行循环)

for (iterator iter = _First; iter = _End; ++ iter)

{

*iter = some_value;

}

如果但从这个简单例子来看,foreach的意义不大。但是

1)很多时候上式中的iterator很长,比如:std::vector<std::vector<std::string> >::iteator。写起来非常麻烦,虽然我们可以typedef,但是过几天你就忘记对应的typedef名字了。

2)因为iterator的类型是对应容器的,所以换个容器,比如原先是std::vector<std::string>,现在改称std::list<std::string>,所有的循环的iterator都要改。而且从循环本身来讲,他应该只是关注数据类型本身,而不是存储数据的容器类型。


要实现一个foreach最重要的就是如果通过一个不知道类型的迭代器来获取其对应的数据类型;C++的新标准添加了typeof关键字,解决了这个问题,但是现在大家基本上都使用的是老的C++标准来开发……

标准库提供了一个std::for_each(transform)函数,可以很方便的遍利。但是需要提供一个额外的function或者function object;功能非常有限。

boost提供了一个BOOST_FOR_EACH宏,虽然很大的提高了编写循环的效率。但是boost库太大,不能为了一个for_each就装一个boost,太划不来了。


下面我提供了一个循环的方法

1)支持break,continue

int a[] = {1, 2, 3, 4, 5};foreach (int x, a, a + 5)std::cout<<x<<std::endl; // 这里可以不用{}foreach(int x, a, a + 5){  if (x == 4) break;  if (x == 2) continue;  std::cout<<x<<std::endl;}

输出 1 3 


2)支持引用。

std::list<int> b = {1, 2, 3}; // 示意foreach(int& i, b.begin(), b.end()) ++ i;foreach(int& i, b.begin(), b.end()) std::cout<<i<<std::endl;

输出2 3 4


3)支持嵌套foreach

int a [3][3] = {1, 2, 3, ....}foreach(int*p1, a, a + 3)  foreach(int i, p1, p1 + 3)    std::cout<<i<<std::endl;

输出1  2 3 4 ……


使用很简单,就如同我们设想的一样,下面是实现原理,看起来比较复杂(当然相对于boost这还是小意思)。

// help classtemplate<size_t _Iter_size>struct __range{// end used for make sure iter type is sametemplate<typename _Iter>__range(_Iter cur, _Iter end)  {new (xxx)_Iter(cur); // 获取直接memcpy应该也能搞定first = true;}template<typename _Iter>bool hasNext(_Iter _End) const{return *((_Iter*)xxx) != _End;}// param is used for return typetemplate<typename _Iter>_Iter moveNext(_Iter) {return (*((_Iter*)xxx))++;}operator bool(){bool b = first;first = false;return b;}char xxx[_Iter_size]; // 保存迭代器,一般情况下,迭代器占用的内存大小应该是固定的bool first;};#define foreach(_Value, _First, _End) \for (__range<sizeof(_First)> __r(_First, _End); __r; ) \for (int __c = 1, __b = 1; __c && __r.hasNext(_End); (__c = (__b == 0) ? 1 : 0), (__b = 1)) \for (_Value = *__r.moveNext(_End); __c --; -- __b)

首先认识一下帮助类(__range);这个类主要是用来维护当前的迭代位置的。提供了三个函数

moveNext 当前迭代器移动到下一个位置,但是返回的仍然是一开始的位置

hasNext 是否到了最后一个位置了

operaor bool() 只是第一次调用返回true,其他时候返回false


下面的foreach 宏,看起来有三重循环,其实算法还是O(N)的。

最外层循环只是为了定义一个__range结构。其实这个循环我觉得可以用if替代。

第二层循环控制循环,判断是否遍历完。

__c参数是为了第三层循环第一次能执行,第二次不能执行

__b参数是为了满足break关键字,因为使用break关键字之后,__b仍是1,否则就会变成0

第三层循环最重要的是一条赋值语句。

关于执行过程(包括break)我就不罗嗦了,有兴趣的自己分析(如有问题欢迎指出)。


当然这个方法除掉额外的一些判断消耗之外,还有一个潜在的问题是编译器会不会对__b, __c的一些操作进行优化,导致未知的错误(这个没有验证过,希望以后有空验证);当然该方法最大的硬伤是你的程序不能有__r __b __c这些变量(一般也没人使用这种名字的变量,如果不放心,就把名字取得更复杂些);当然如果双层foreach的话,应该是内层foreach会屏蔽掉外层foreach中的__r,__b,__c


有一点需要注意的就是_Value中不能包含','比如:

foreach (std::pair<int, int>& x, v.begin(), v.end()) ;

就会出现错误,因为编译器会以为给foreach宏提供了4个参数。

这种情况下只能:

typedef std::pair<int, int> int_pair;

foreach (int_pair& x, v.begin(), v.end())


原创,如有引用请标注本贴地址。谢谢!

原创粉丝点击