The c++11 supports range-based for loops. This is a very convenient feature that can save a lot of code. The following code makes it easy to traverse the elements in the vector and print them out:
12345678 |
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; } |
The objects that can be traversed include:
- Array. (not including pointers)
- Defines the begin () and end () methods, and returns the class object for which the method returns an iterator. (All containers in STL are available)
(for a dynamically generated array traversal, you can save a lot of code with the range class described below)
Refer to Http://en.cppreference.com/w/cpp/language/range-for,
Statement for (
range_declaration :
range_expression )
loop_statement
is equivalent to the following statement:
123456789 |
{
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]中的值。(Boost库有irange类可以满足这个需求,但是下面会讨论下我的实现)
最直接的方法,就是写一个函数,返回一个vector<int>对象,其元素为从begin到end的值。但这样每次循环时都得构造一个这样的对象,略慢。
从标准来看,如果一个类要支持这样遍历,至少得有begin()和end()方法。在for循环的初始化部分,调用了这两个方法之后,就没这个类啥事了——都是迭代器的事。所以很自然,从迭代器上下手。这个迭代器必须支持三种操作:!=,前缀++,解引用。有没发现,如果这个“迭代器”是个int数值的话,上面循环中的__begin!=__end;++__begin语句就是一个非常自然的实现。现在的目标很简单了:这个“迭代器”不遍历容器中的每个元素,而就是一个简单的int数值的封装。对其解引用将返回这个数,而比较和自加操作均对这个数进行操作。
有想法之后,实现起来就很容易了。首先定义一个仿迭代器 FakeIter ,其对一个数值进行封装,并重载必须的操作符。
12345678910111213141516171819202122232425262728 |
class
FakeIter
{
typedef
long
_VType;
//数值的类型为long。当然了,也可以写个模板出来
public
:
explicit
FakeIter(_VType val)
:value_(val){}
bool
operator != (
const
FakeIter& other)
const
{
return
(
this
->GetValue()) != (other.GetValue());
}
_VType operator* ()
const
{
return
GetValue();
}
const FakeIter& operator++ ()
{
++value_;
return
*
this
;
}
private
:
_VType GetValue()
const
{
return
value_;
}
_VType value_;
};
|
As for the implementation of the "container" class, it is easier to implement the Begin () and end () methods and return to the above fakeiter. The methods in the class add some cout statements to better understand the invocation process of the concrete method during loop execution, and the actual time can be erased.
12345678910111213141516171819202122232425262728 |
class Range
{
typedef
long
_VType;
//同样,也可以弄个模板出来,但是就不方便用了
public
:
Range (_VType begin_v, _VType end_v)
:begin_value_(begin_v), end_value_(end_v)
{
cout<<
"Range::Range()"
<<endl;
}
~Range()
{
cout<<
"Range::~Range()"
<<endl;
}
FakeIter begin ()
const
{
cout<<
"Range::begin()"
<<endl;
return
FakeIter(begin_value_);
}
FakeIter end ()
const
{
cout<<
"Range::end()"
<<endl;
return
FakeIter(end_value_ );
}
private
:
_VType begin_value_;
_VType end_value_;
};
|
All right, try this. It's useless:
12345 |
for ( auto x: Range(1,5)) { std::cout<<x<<endl; } std::cout<< "Loop end" <<endl; |
The output under vs2012 and Clang is as follows:
Range::range ()
Range::begin ()
Range::end ()
1
2
3
4
Range::~range ()
Loop End
Well, it looks like it's going to work if you drop the comment out of the output statement that affects the line of sight. Now to traverse a new generated array, just use the range subscript and the world will be quiet.
But the stride! Well, I don't think I have that kind of demand for the moment. But the implementation is also very simple: Modify the Fakeiter class, you can add a member to represent the step, and then modify the self-add operation. Further, you can add a generator method to become a more generic generator. With the c++11 lambda operator, it is also very handy.
C++11 for Loop, and implementation of the scope range class