Seaside Laboratory

Posts

C++ の auto と foreach マクロ

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

std::vector< int > nNumbers;

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

普通の for 文とは以下の点が大きく異なる。

  • iterator に大小はないので条件式の比較演算子は "<" ではなく "!=" を使う。
  • 次の要素への移動は一時オブジェクトが生成されない前置インクリメントで行う。
  • 各種項目の宣言がやたらと長く記述量が多い。

真面目に for 文を書いていると疲れてしまうので、以下のような foreach マクロを自作して対処していた。

// 型、イテレーター、コンテナの順で渡す
#define foreach( t, i, c ) for ( t::iterator i = c.begin(); i != c.end(); ++i )
// const 版
#define const_foreach( t, i, c ) for ( t::const_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 )
// const 版
#define const_foreach( i, c ) for ( auto i = c.begin(); i != c.end(); ++i )

std::vector< int > nNumbers;

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

ただ、型宣言が省略された影響で foreach と const_foreach の内容が同じになってしまっている。begin の戻り値を使って型推論を行うので対処のしようがないと思っていたが、調べてみると const 版の begin と end が用意されていた。

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

つまり先程のマクロは、

#define const_foreach( i, c ) for ( auto i = c.cbegin(); i != c.cend(); ++i )

と書き換えてやればよい。

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