1. 程式人生 > >STL/Boost C++ 11 中foreach的用法

STL/Boost C++ 11 中foreach的用法

本篇將對C++ 標準庫中的兩種foreach,以及boost中的BOOST_FOREACH進行講解說明

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <boost/foreach.hpp>

// 遍歷STL標準庫容器
int main()
{
	std::vector<int> vec{ 1,2,3,4,5 };

	// 1. C++ 中標準用法 
	for (int &num : vec)
	{
		num *= 2;
	}
	// 2. algorithm 中的 for_each
	std::for_each(begin(vec), end(vec), [](int num) {std::cout << num << " "; }); // 寫法1
	std::cout << std::endl;

	std::for_each(vec.cbegin(), vec.cend(), [](int num) {std::cout << num << " "; });	// 寫法2, 多一個c字首表示const
	std::cout << std::endl;

	std::for_each(vec.rbegin(), vec.rend(), [](int num) {std::cout << num << " "; });	// 反向遍歷,r字首表示反向,r和c可以組合使用
	std::cout << std::endl;
	/*
	這種寫法可能被廢棄了,在VS2017下報錯
	for each (object var in collection_to_loop)
	{

	}
	*/

	// 3.BOOST_FOREACH 
	// 與第1種用法類似 for(object var: collection_to_loop)
	BOOST_FOREACH(int &num, vec)	// 可以使用引用訪問
	{
		num += 1;
	}

	int num;	
	BOOST_FOREACH(num, vec) // 可以將 int num 定義寫在外邊
	{
		std::cout << num << " ";
	}
	std::cout << "\n";		
	// 反向遍歷
	BOOST_REVERSE_FOREACH(const auto & num, vec) // 可以寫int num, auto num, const int num, int &num等,都沒有問題
	{
		std::cout << num << " ";
	}

	std::cin.get();
	return 0;
}

以下是官方提供的 Range-based for Statement (C++) 示例程式碼

// range-based-for.cpp  
// compile by using: cl /EHsc /nologo /W4  
#include <iostream>  
#include <vector>  
using namespace std;  
  
int main()   
{  
    // Basic 10-element integer array.  
    int x[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };  
  
    // Range-based for loop to iterate through the array.  
    for( int y : x ) { // Access by value using a copy declared as a specific type.   
                       // Not preferred.  
        cout << y << " ";  
    }  
    cout << endl;  
  
    // The auto keyword causes type inference to be used. Preferred.  
  
    for( auto y : x ) { // Copy of 'x', almost always undesirable  
        cout << y << " ";  
    }  
    cout << endl;  
  
    for( auto &y : x ) { // Type inference by reference.  
        // Observes and/or modifies in-place. Preferred when modify is needed.  
        cout << y << " ";  
    }  
    cout << endl;  
  
    for( const auto &y : x ) { // Type inference by reference.  
        // Observes in-place. Preferred when no modify is needed.  
        cout << y << " ";  
    }  
    cout << endl;  
    cout << "end of integer array test" << endl;  
    cout << endl;  
  
    // Create a vector object that contains 10 elements.  
    vector<double> v;  
    for (int i = 0; i < 10; ++i) {  
        v.push_back(i + 0.14159);  
    }  
  
    // Range-based for loop to iterate through the vector, observing in-place.  
    for( const auto &j : v ) {  
        cout << j << " ";  
    }  
    cout << endl;  
    cout << "end of vector test" << endl;  
}  

經過測試,將上邊示例中for換成BOOST_FOREACH, for中的冒號換成逗號,發現程式仍然正常執行,說明兩者是等價的。

下面列舉一些BOOST_FOREACH的用法示例

1. 可訪問std::string

#include <string>
#include <iostream>
#include <boost/foreach.hpp>

int main()
{
    std::string hello( "Hello, world!" );

    BOOST_FOREACH( char ch, hello )
    {
        std::cout << ch;
    }

    return 0;
}

2.支援C風格的陣列,下面是一些示例展示了BOOST_FOREACH的基本用法

#include <iostream>  
#include <vector>  
#include <boost/foreach.hpp>
#include <list>
#include <queue>
using namespace std;

void main()
{
	// 1. 訪問STL容器
	std::list<int> list_int{ 1,2,3,4 };
	BOOST_FOREACH(int i, list_int)
	{
		// do something with i
	}

	// 2. 可以訪問C風格的陣列
	short array_short[] = { 1,2,3 };
	BOOST_FOREACH(int i, array_short)
	{
		// The short was implicitly converted to an int
	}

	// 3. int i可前向定義,BOOST_FOREACH 支援關鍵字continue,break,return
	std::deque<int> deque_int{ 4,5,6,1,7,8,2,9 };
	//std::deque<int> deque_int{ 4,5,6,1,7,8,0,9 };
	int i = 0;
	BOOST_FOREACH(i, deque_int)
	{
		if (i == 0) return;
		if (i == 1) continue;
		if (i == 2) break;
		std::cout << i << " ";
	}

	// 4. 引用訪問C陣列
	short array_short[] = { 1, 2, 3 };
	BOOST_FOREACH(short & i, array_short)
	{
		++i;
	}
	//  array_short[] = { 2, 3, 4 };

	// 5. 使用巢狀的BOOST_FOREACH, 此處可以發現,BOOST_FOREACH的大括號不是必須的
	std::vector<std::vector<int> > matrix_int;
	BOOST_FOREACH(std::vector<int> & row, matrix_int)
		BOOST_FOREACH(int & i, row)
		++i;

	// 6. 迴圈體中可以是一個函式,函式返回值是要遍歷的容器
	extern std::vector<float> get_vector_float();
	BOOST_FOREACH(float f, get_vector_float())
	{
		// 注意:此處的 get_vector_float() 函式只會被呼叫一次
		// 可以這樣測試:讓函式get_vector_float()每一次呼叫都返回不一樣的值
	}

	// 7. 反向遍歷
	std::list<int> list_int( /*...*/);
	BOOST_REVERSE_FOREACH(int i, list_int)
	{
		// do something with i
	}


	std::cin.get();
}

有一點需要注意的是,有時候我們覺得BOOST_FOREACH寫起來比較長,不方便,於是我們就定義了以下的巨集來代替BOOST_FOREACH:

#define foreach   BOOST_FOREACH

於是有了下面的程式碼

#include <iostream>
#include <boost/foreach.hpp>

//using namespace boost;
#define foreach   BOOST_FOREACH

void main()
{
	int arr[]{ 1,2,3,4,5 };

	foreach(int i, arr)
	{
		std::cout << i << " ";
	}


	std::cin.get();
}

執行起來確實也沒問題,但是官方推薦儘量使用下面的巨集定義

#define foreach_         BOOST_FOREACH
#define foreach_r_       BOOST_REVERSE_FOREACH

因為在boost/foreach.hpp中foreach是一個namespace,有很多人巨集都是用的 #define foreach  BOOST_FOREACH,這樣可能會出問題,我測試了當我using namespace boost 後,程式仍然正確執行,但並不表示所有情況都正確,因為foreach是一個比較常用的關鍵詞,比如在Qt 中也有foreach,這樣在某些情況下可能會有未知的錯誤,所以推薦使用帶下劃線的foreach巨集定義

// boost/foreach.hpp
namespace foreach
{
    ///////////////////////////////////////////////////////////////////////////////
    // in_range
    //
    template<typename T>
    inline std::pair<T, T> in_range(T begin, T end)
    {
        return std::make_pair(begin, end);
    }

    //.....

} // namespace foreach