Seaside Laboratory

Posts

foreach マクロと auto

STL コンテナは便利だが、イテレーションは面倒である。

std::vector< int > nNumbers;

for ( std::vector< int >::iterator i = nNumbers.begin(); i != nNumbers.end(); ++i )
{
    // 何らかの処理
}

普通の for 文と違い、以下の点に気を付けなければいけない。

  • iterator に大小はないので条件は "<" ではなく "!=" で判定する必要あり。
  • 次要素への移動はコストの低い前置インクリメントが望ましい。

for 文が必要になる度に注意しながら書くのが面倒なのと、STL 特有の長い記述は書いていて疲れるので foreach マクロを自作して対処していた。

// 型、イテレーター、コンテナの順で渡す
#define foreach( t, i, c ) for ( t::iterator i = c.begin(); i != c.end(); ++i )

std::vector< int > nNumbers;

foreach ( std::vector< int >, i, nNumbers )
{
    // 何らかの処理
}

簡素に書け、記述ミスの起こらないこのマクロを長年愛用していたが、map のように宣言にカンマを含んでいる型はマクロが誤判別してしまうので、事前に typedef しなければいけないという欠点がある。Boost にも同様の機能を持つ BOOST_FOREACH マクロが用意されているが、コンテナ型の記述は不要なものの、値を受け取り時の pair 宣言で同様の問題が発生する。

VC++ 2010 から C++11 で追加された型推論の auto をサポートしているので、先程のマクロはより簡素な形式で書くことができるようになった。typedef も不要。

// イテレーター、コンテナの順で渡す
#define foreach( i, c ) for ( auto i = c.begin(); i != c.end(); ++i )

std::vector< int > nNumbers;

foreach ( i, nNumbers )
{
    // 何らかの処理
}

const_iterator を使いたい時、以前は以下のようなマクロを使っていたが、

#define const_foreach( t, i, c ) for ( t::const_iterator i = c.begin(); i != c.end(); ++i )

begin は iterator を返すので、auto にすると const_iterator を取得することができない。調べてみると、const 版の begin と end が用意されている模様。

種別 反復子 始端 終端
昇順 iterator begin end
逆順 reverse_iterator rbegin rend
昇順 (const) const_iterator cbegin cend
逆順 (const) const_reverse_iterator crbegin crend

VC++ 2010 は range-based for をサポートしていないので当分の間はこのマクロを使う予定。