一. 基於范圍的for循環簡介
在C++03/98中,不同的容器和數組,遍歷的方法不盡相同,寫法不統一,也不夠簡潔,而C++11基於范圍的for循環以統一,簡潔的方式來遍歷容器和數組,用起來更方便了。
數組循環:
1 using namespace std;
2
3 const int size = 5;
4 int* p = new int[size]{1,2,3,4,5};
5 for(int i =0;i<size;i++){
6 cout<<p[i]<<" ";
7 }
容器循環:
1 using namespace std;
2
3 vector<int> vec;
4 for (auto it=vec.begin(),it!=vec.end();it++){
5 cout<<*it<<" ";
6 }
當然,<algorithm>中還有一個for_each算法可以用來對容器進行遍歷
Function for_each( InputIterator begin, InputIterator end, Function f ) {
while ( begin != end )
f( *begin++ );
}
1 using namespace std;
2
3 void do_cout(int& num){
4 cout<<num<<" ";
5 }
6
7 vector<int> vec;
8 for_each(vec.begin(),vec.end(),do_cout);
for_each
優點:不再需要關注迭代器(Iterator)的概念
缺點:必須顯示的給出容器的開頭(Begin)和結尾(End)
在C++11中終於有基於范圍的for循環(The range-based for statement)。
1 #include <iostream>
2 #include <vector>
3
4 using namespace std;
5
6 int main(){
7 vector<int> arr = {1, 2, 3};
8 for(auto n : arr){
9 cout << n << endl;
10 }
11
12 return 0;
13 }
在上面的基於范圍的for循環中,在n的定義之後,緊跟一個冒號(:),之後直接寫上需要遍歷的表達式,for循環將自動以表達式返回的容器為范圍進行迭代
需要注意
1.在上面的例子中,我們都是在使用只讀方式遍歷容器。如果需要在遍歷時修改容器中的值,則需要如下使用引用。
1 for ( auto& n : arr){
2 cout<< n++ << endl;
3 }
2.若只是希望遍歷,而不希望修改,可以使用const auto&來定義n的類型。這樣對於復制負擔比較大的容器元素(比如一個std::vector<std::string>數組)也可以無損耗地進行遍歷。
1 #include <iostream>
2 #include <string>
3 #include <vector>
4
5 using namespace std;
6
7 int main(){
8 vector<string> arr={"li ming","wang lei","han meimei"};
9 for(const auto& n : arr){
10 cout<<arr<<endl;
11 }
12 return 0;
13 }
二.基於范圍的for循環的使用細節
1.
1 #include <iostream>
2 #include <map>
3
4 using namespace std;
5
6 int main(){
7 map<string,int> ma={
8 {"1",1},{"2",2},{"3",3}
9 };
10 for(auto & val : mm){
11 cout<< val.first <<"->" << val.second <<endl;
12 }
13 return 0;
14 }
1).for循環中val的類型是std::pair.因此,對於map這種關聯性容器而言,需要使用val.first或val.second來提取鍵值。
2).auto自動推導師出的類型是容器中的value_type,而不是迭代器。
2.
1 #include <iostream>
2 #include <set>
3
4 using namespace std;
5
6 int main(){
7 set<int> se = {1 , 2, 3};
8 for (auto& val : ss){
9 //error:increment of read-only reference 'val'
10 cout<< val++ << Lendl;
11 }
12 return 0;
13 }
在使用基於范圍的for循環時,還需要注意容器本身的一些約束。
1).例子中,auto & 定義了std::set<int>中元素的引用,希望能夠在循環中對set的值進行修改,但std::set的內部元素是只讀的,因此,for循環中的auto& 會被推導為const int&。
2).在std::map的遍歷中,基於范圍白for循環中的std::pair引用,是不能夠修改first的。
3.
1 #include <iostream>
2 #include <vector>
3
4 using namespace std;
5
6 vector<int> arr = { 1, 2, 3, 4, 5};
7
8 vector<int>& get_range(){
9 cout<<"get_range ->: "<< endl;
10 return arr;
11 }
12
13 int main(){
14 for(auto val : get_range()){
15 cout<< val << endl;
16 }
17 return 0;
18 }
輸出結果:
get_range ->:
1
2
3
4
5
1).從上面的例子可以看到,無論基於范圍的for循環迭代了多少次,冒號後面的表達式只會執行一次,只會在第一次迭代之前調用。
4.基於范圍的for循環等價的普通的for循環如下:
1 #include <iostream>
2 #include <vector>
3
4 using namespace std;
5
6 int main(){
7 vector<int> arr = {1, 2, 3, 4, 5};
8
9 auto && __range = (arr);
10 for( auto __begin = __range.begin(), __end = __range.end();__begin!=__end;++__begin(){
11 auto val = *__begin;
12 cout<< val <<endl;
13 arr.push_back(0);
14 }
15 return 0;
16 }
1).基於范圍的for循環其實是普通for循環的語法糖,從上面的代碼可以清晰地看到,和我們平時寫的遍歷容器不同,基於范圍的for循環傾向於在循環開始之前確定好迭代的范圍,而不是在每次迭代之前都去調用一次arr.end()。
三.讓基於范圍的for循環支持自定義類型
在之前提及的vector,set,map,都是標准模板庫中的容器。都實現了begin,end等函數。那麼對於我們自己定義的容器類,如何才能讓它支持range-based for呢?下面是書中的一個樣例。
1 //迭代器類的實現
2
3 namespace detail_range{
4
5 template<typename T>
6 class iterator
7 {
8 public:
9 using value_type = T;
10 using size_type = size_t;
11
12 iterator(size_type cur_start, value_type begin_val, value_type step_val)
13 :cursor_(cur_start),step_(step_val),value_(begin_val){
14 value_ += (step_ * cursor_);
15 }
16
17 value_type operator*() const{
18 return value_;
19 }
20
21 bool operator!=(const iterator& hrs) const{
22 return (cursor_ != rhs.cursor_);
23 }
24
25 iterator& operator++(void) //prefix ++ operator only
26 value_ += step_;
27 ++ cursor_;
28 return (*this);
29 }
30
31 private:
32 size_type cursor_;
33 const value_type step_;
34 value_type value_;
35 };
36
37 }//namespace detail_range
C++11,定義模板的別名只能使用using。定義一般類型的別名時與typedef沒有區別。
//impl為要實現的類似容器的類,我們給它定義了容器所要擁有的基本的概念抽象
namespace detail_range{
template<typename T>
class imply
{
public:
using value_type = T;
using reference = const value_type&;
using const_reference = const value_type&;
using iterator = const detail_range::iterator<value_type>;
using const_iterator = const detail_range::iterator<value_type>;
using size_type = typename iterator::size_type;
impl(value_type begin_val, value_type end_val, value_type step_val)
: begin_(begin_val),end_(end_val),step_(step_val),max_count_(get_adjusted_count()){}
size_type size(void) const{
return max_count_;
}
const_iterator begin(void) const{
return { 0, begin_, step_ };
}
const_iterator end(void) const{
return {max_count_ , begin_, step_ };
}
private:
const value_type begin_;
const value_type end_;
const value_type step_;
const size_type max_count_;
size_type get_adjusted_count(void) const{
if(step_ > 0 && begin_ >= end_)
throw std::logic_error("End value must be greater than begin value.");
else if(step_ < 0 && begin_ <= end_)
throw std::logic_error("End value must be less than begin value.");
size_type x = static_cast<size_type>((end_ - begin_)/step_);
if( begin_+ (step_ * x) != end_)
++ x;
return x;
}
};
}//namespace detail_range
max_count_為最大迭代次數,通過調用get_adjusted_count函數獲得,他會先判斷begin_,end_和step_的合法性。
1 template<typename T>
2 detail_range::imply<T> range(T end){
3 return { {}, end, 1 };//使用初始化列表
4 }
5
6 template<typename T>
7 detail_range::impl<T> range(T begin, T end){
8 return { begin, end, 1};
9 }
10
11 template <typename T, typename U>
12 auto range(T begin, T end, U step)->detail_range::impl<decltype(begin+step)>{
13 using r_t = detail_range::impl<decltype(begin + step)>;
14 return r_t(begin, end, step);
15 }
1 #include <iostream>
2
3 using namespace std;
4
5 int main(){
6 cout << "range(15):";
7 for(int i : range(15)){
8 cout << " " << i;
9 }
10 cout<<endl;
11
12 cout << "range(2,6):";
13 for( auto i : range(2, 6)){
14 cout << " " << i;
15 }
16 cout<<endl;
17
18 const int x = 2, y = 6, z = 3;
19 cout << "range(2,6,3):";
20 for (auto i : range(x,y,z)){
21 cout << " " << i;
22 }
23 cout<<endl;
24
25 cout << "range(-2,-6,-3):";
26 for (auto i : range(-2,-6,-3)){
27 cout << " " << i;
28 }
29 cout<<endl;
30
31 cout << "range(10.5,10.5):";
32 for (auto i : range(10.5,10.5)){
33 cout << " " << i;
34 }
35 cout<<endl;
36
37 cout << "range(35,27,-1):";
38 for (auto i : range(35,27,-1)){
39 cout << " " << i;
40 }
41 cout<<endl;
42
43 cout << "range(2,8,0.5):";
44 for (auto i : range(2,8,0.5)){
45 cout << " " << i;
46 }
47 cout<<endl;
48
49 cout << "range(8,7,-0.1):";
50 for (auto i : range(8,7,-0.1)){
51 cout << " " << i;
52 }
53 cout<<endl;
54
55 cout << "range('a','z'):";
56 for (auto i : range('a','z')){
57 cout << " " << i;
58 }
59 cout<<endl;
60
61 return 0;
62 }
63
64 //示例運行的結果如下:
65 //range(15): 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
66 //range(2, 6): 2 3 4 5
67 //range(2, 6, 3): 2 5
68 //range(-2, -6, -3): -2 -5
69 //range(10.5, 10.5): 10.5 11.5 12.5 13.5 14.5
70 //range(35, 27, -1): 35 34 33 32 31 30 29 28
71 //range(2, 8, 0.5): 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5
72 //range(8, 7, -0.1): 8 7.9 7.8 7.7 7.6 7.5 7.4 7.3 7.2 7.1
73 //range('a', 'z'): a b c d e f g h i j k l m n o p q r s t u v w x y