C++11 范围for的实现原理

       C++11支持range-based for循环。这是一个很方便的特性,能省挺多代码。以下代码就能很方便的遍历vector中的元素,并打印出来:

1
2
3
4
5
6
7
8
9
std::vector<int> int_vec;
int_vec.push_back(1);
int_vec.push_back(2);
//如果要修改int_vec中的元素,将变量x声明为 int& 即可
for(int x: int_vec) 
{
  std::cout << x << endl;
}

       可以遍历的对象包括:
       •数组(不包括指针)。
       •定义了begin()和end()方法,且返回该方法返回迭代器的类对象(STL 中所有容器都可以)。
       参考 http://en.cppreference.com/w/cpp/language/range-for 可知,语句 for ( range_declaration : range_expression) loop_statement 与以下语句作用等价:

1
2
3
4
5
6
7
8
9
10
{
  auto && __range = range_expression;
  for(auto __begin = begin_expr,
    __end = end_expr;
    __begin != __end; ++__begin)
{
   range_declaration = *__begin;
  loop_statement
  }
}

       对于可遍历的类对象,begin和end分别由类的begin()和end()方法产生。且由于range变量是右值引用,如果range_expression的结果是右值,其将会在循环结束后析构。
       这样,C++11终于支持了这种现代编程语言都支持的遍历方式了。但是,无论是语法还是标准库都不支持对具体数字的遍历,比如python中的for i in xrange(1,5)语句中,x将连续取[1,4]中的值。
       最直接的方法,就是写一个函数,返回一个vector对象,其元素为从begin到end的值。但这样每次循环时都得构造一个这样的对象,略慢。
       从标准来看,如果一个类要支持这样遍历,至少得有begin()和end()方法。在for循环的初始化部分,调用了这两个方法之后,就没这个类啥事了——都是迭代器的事。所以很自然,从迭代器上下手。这个迭代器必须支持三种操作:!=,前缀++,解引用。有没发现,如果这个“迭代器”是个int数值的话,上面循环中的
begin!=end;++begin语句就是一个非常自然的实现。现在的目标很简单了:这个“迭代器”不遍历容器中的每个元素,而就是一个简单的int数值的封装。对其解引用将返回这个数,而比较和自加操作均对这个数进行操作。
       有想法之后,实现起来就很容易了。首先定义一个仿迭代器 Iter ,其对一个数值进行封装,并重载必须的操作符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Iter
{
public:
typedef long value_type;
public:
explicit Iter(value_type val)
: value_(val)
{
}
bool operator != (const Iter& other) const
{
return value_ != other.value_;
}
value_type& operator*()
{
return value_;
}
const value_type& operator*() const
{
return value_;
}
Iter& operator++ ()
{
++value_;
return *this;
}
Iter operator++ (int)
{
Iter tmp = *this;
++value_;
return tmp;
}
private:
value_type value_;
};

       至于“容器”类的实现,就更简单了:实现begin()和end()方法,并返回上面的 Iter 就好了。类中的方法加了一些cout语句,可以更清楚的了解循环执行时具体方法的调用过程,实际用时可以删掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Range
{
using iterator = Iter;
public:
Range(iterator::value_type begin, iterator::value_type end)
: begin_value_(begin), end_value_(end)
{
std::cout << "Range::Range()" << std::endl;
}
~Range()
{
std::cout << "Range::~Range()" << std::endl;
}
iterator begin() const
{
std::cout << "Range::begin()" << std::endl;
return iterator(begin_value_);
}
iterator end() const
{
std::cout << "Range::end()" << std::endl;
return iterator(end_value_ + 1);
}
private:
iterator::value_type begin_value_;
iterator::value_type end_value_;
};

       好了,试试看这货有没用:

1
2
3
4
5
6
7
8
9
10
int main()
{
for(auto x : Range(1, 2))
{
std::cout << x << std::endl;
}
std::cout << "Loop end" << std::endl;
return 0;
}

       输出如下:

1
2
3
4
5
6
7
8
9
10
Range::Range()
Range::begin()
Range::end()
1
2
3
4
5
Range::~Range()
Loop end

文章目录