C ++ 11 for loop, and the implementation of the Range class, forrange
C ++ 11 supports the range-based for loop. This is a convenient feature that saves a lot of code. The following code can easily 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); // If You Want To modify the element in int_vec, declare variable x as int &. for ( int x: int_vec) { std::cout << x << endl; } |
Objects that can be traversed include:
- Array. (Excluding pointers)
- Defines the begin () and end () methods, and returns the Class Object of the iterator returned by this method. (All containers in STL can)
(For the traversal of dynamically generated arrays, the Range class described below can save a lot of code)
See the http://en.cppreference.com/w/cpp/language/range-for,
Statementfor (
Range_declaration:
Range_expression)
Loop_statement
It 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 } } |
For class objects that can be traversed, __begin and _ end are generated by the begin () and end () Methods of the class respectively. The _ range variable references the right value. If range_expression returns the right value, it will analyze the structure after the loop ends.
In this way, C ++ 11 finally supports the Traversal method supported by modern programming languages. However, neither the syntax nor the standard library supportsNumberFor example, in the for I in xrange () Statement in python, x will consecutively take the value in. (The Boost library has an irange class that can meet this requirement, but my implementation will be discussed below)
The most direct method is to write a function and return a vector <int> object. Its element is the value from begin to end. In this way, such an object must be constructed for each loop, which is a little slow.
From the standard perspective, if a class must support such traversal, at leastBegin () and end ()Method. In the initialization Part Of The for loop, after the two methods are called, nothing happens to this class --It's all about the iterator.. So naturally, the iterator is used up and down. This iterator must support three operations:! =, Prefix ++, unreference. No. If the iterator is an int value, _ begin the above loop! The =__ end; ++ _ begin statement is a very natural implementation. The current goal is simple: This "iterator" does not traverse every element in the container, but isEncapsulation of simple int values. This number is returned for its unreferenced operation, and the comparison and auto-increment operations operate on this number.
Once you have an idea, it is easy to implement it. First, define a quasi-iterator FakeIter, which encapsulates a value and reloads the necessary operators.
12345678910111213141516171819202122232425262728 |
class FakeIter { typedef long _VType; // The value type is long. Of course, you can also write a template. 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_; }; |
The implementation of the "Container" class is simpler: implement the begin () and end () methods, and return the above FakeIter. Some cout statements are added to the methods in the class, so that you can better understand the calling process of specific methods during cyclic execution. You can delete them in actual use.
12345678910111213141516171819202122232425262728 |
class Range { typedef long _VType; // Similarly, you can create a template, but it is not convenient to use. 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_; }; |
Okay. Try this product. It's useless:
12345 |
for ( auto x: Range(1,5)) { std::cout<<x<<endl; } std::cout<< "Loop end" <<endl; |
Output in vs2012 and clang is as follows:
Range::Range()
Range::begin()
Range::end()
1
2
3
4
Range::~Range()
Loop end
Well, if you comment out the output statements that affect the line of sight, it seems to be usable. Now, if we want to traverse an array generated by new, we only need to use this array to set a subscript, and the world will be quiet.
But what about the step size! Well, it seems that I have no such requirement yet. However, it is easy to implement: you can modify the FakeIter class. You can add a member indicating the step size and then modify the auto-increment operation. Furthermore, you can add a generator method to become a more common generator. It is convenient to use with the C ++ 11 lambda operator.