Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Chapter 6. Boost.Foreach

Eric Niebler

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

目錄

簡介
可擴展性
可移植性
缺陷
歷史和鳴謝

簡介

Make simple things easy.
-- Larry Wall

 BOOST_FOREACH 是什麼?

在C++中,寫一個循環去迭代一個序列是很單調的。我們可以用迭代器,這需要相當大量的代碼,或者可以用 std::for_each() 算法,將我們的循環體移到一個謂詞中,這樣並沒有減少代碼量,還使得我們的邏輯遠離於使用的地點。作為對比,其它一些語言,如 Perl,就提供了專門的 "foreach" 結構來自動進行這一過程。BOOST_FOREACH 正是在C++中的一個同樣作用的結構。它為我們迭代一個序列,不需要我們來直接處理迭代器或編寫謂詞。

BOOST_FOREACH 是為了易用性和高效性而設計的。它不進行動態的內存分配,沒有虛擬函數調用或通過函數指針的調用,也沒有令編譯器的優化器無法處理的調用。這樣可以生成近似於最優化的代碼;BOOST_FOREACH 的性能通常只比手工編寫的循環差幾個百分點。而且雖然 BOOST_FOREACH 是一個宏,但它是有良好行為的。它只會對其參數進行一次求值,不會有令人討厭的問題。

Hello, world!

以下是一個例子,使用了 BOOST_FOREACH 來迭代一個 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;
}

程序的輸出如下:

Hello, world!

支持的序列類型

BOOST_FOREACH 用於迭代某個序列。但是這個序列有什麼要求呢?由於 BOOST_FOREACH 是構建在 Boost.Range 之上的,所以它自動支持了那些被 Boost.Range 認可的序列。特定地,BOOST_FOREACH 可用於滿足 單遍區間概念 的類型。例如,我們可以將 BOOST_FOREACH 用於:

  • STL 容器
  • 數組
  • Null-結尾的字符串(charwchar_t)
  • 由迭代器組成的 std::pair
[Note] 備註

對 STL 容器的支持是非常全面的;任何類似於 STL 的容器都可算在內。只要它有嵌套的 iteratorconst_iterator 類型以及 begin()end() 成員函數,BOOST_FOREACH 就可以自動得知如何迭代它。這正是 BOOST_FOREACH 可用於 boost::iterator_range<>boost::sub_range<> 的原因。

關於如何將 BOOST_FOREACH 用於其它類型,請見 可擴展性 一節。

例子

以下是一些例子,示範了我們使用 BOOST_FOREACH 的幾種不同方式。

迭代一個 STL 容器:

std::list<int> list_int( /*...*/ );
BOOST_FOREACH( int i, list_int )
{
    // 用 i 執行某些操作
}

迭代一個數組,具有協變性(即迭代變量的類型與容器的元素類型不完全相同):

short array_short[] = {1,2,3};
BOOST_FOREACH( int i, array_short )
{
    // short 被隱式轉換為 int
}

預先聲明循環變量,並在循環體中使用 break, continue, 和 return

std::deque<int> deque_int( /*...*/ );
int i = 0;
BOOST_FOREACH( i, deque_int )
{
    if( i == 0 ) return;
    if( i == 1 ) continue;
    if( i == 2 ) break;
}

以引用方式迭代一個序列,並修改底層序列:

short array_short[] = { 1, 2, 3 };
BOOST_FOREACH( short & i, array_short )
{
    ++i;
}
// 現在 array_short 包含 {2,3,4}

用嵌套的 BOOST_FOREACH 循環迭代一個 vector 的 vector. 在這個例子中,注意並不需要在循環體外面使用括號:

std::vector<std::vector<int> > matrix_int;
BOOST_FOREACH( std::vector<int> & row, matrix_int )
    BOOST_FOREACH( int & i, row )
        ++i;

迭代一個以值方式(即右值)返回一個序列的函數:

extern std::vector<float> get_vector_float();
BOOST_FOREACH( float f, get_vector_float() )
{
    // 注: get_vector_float() 只會被調用一次
}

以反序遍歷:

std::list<int> list_int( /*...*/ );
BOOST_REVERSE_FOREACH( int i, list_int )
{
// 用 i 來做一些操作
}

在一些舊的編譯器上,不能對右值進行迭代。請查看 可移植性 一節,看看你的編譯器是否支持。

BOOST_FOREACH 更漂亮

有的人對 BOOST_FOREACH 的名字有意見。它太長了。而且大寫字母看起來有點煩人。這可能是真的,但 BOOST_FOREACH 不過是遵從了 Boost 名字規則。但是這並不意味著你必須用它。如果你想使用一個不同的標識符(可能是 foreach),你只要:

#define foreach BOOST_FOREACH
#define reverse_foreach BOOST_REVERSE_FOREACH

你唯一要保證的是,你選用的標識符不會在你的代碼中引起名字衝突。

[Note] 備註

不要用 #define foreach(x,y) BOOST_FOREACH(x,y). 如果參數本身也是宏的話,這會有問題。它會引起這些宏的額外擴展。你應該使用前面所說的形式。

Last revised: November 02, 2008 at 13:06:18 GMT


PrevUpHomeNext