Boost Foreach Libraryは、C++においてシーケンスをループするためのfor each文をライブラリで提供する。
for each文には、BOOST_FOREACH
というマクロを使用する。
BOOST_FOREACH
を使用することで、シーケンスの各要素が順番にループ変数に格納され、処理される。
このマクロを使用するには、<boost/foreach.hpp>
をインクルードする。
#include <iostream>
#include <boost/foreach.hpp>
int main()
{
const int ar[] = {3, 1, 4};
BOOST_FOREACH (int x, ar) {
std::cout << x << std::endl;
}
}
実行結果:
3
1
4
BOOST_FOREACH
マクロは、シーケンスの各要素を参照し、書き換えることができる。
#include <iostream>
#include <boost/foreach.hpp>
int main()
{
int ar[] = {3, 1, 4};
BOOST_FOREACH (int& x, ar) {
++x; // 要素を書き換える
}
BOOST_FOREACH (int x, ar) {
std::cout << x << std::endl;
}
}
実行結果:
4
2
5
BOOST_FOREACH
マクロは、組み込み配列だけではなく、std::vector
やstd::list
、std::map
といった標準ライブラリのコンテナを処理することができる。
#include <iostream>
#include <vector>
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
int main()
{
const std::vector<int> v = boost::assign::list_of(3)(1)(4);
BOOST_FOREACH (int x, v) { // std::vectorをループする
std::cout << x << std::endl;
}
}
実行結果:
3
1
4
BOOST_FOREACH
マクロでstd::map
を処理するには、少し工夫が必要になる。
BOOST_FOREACH
マクロの第1引数には、std::map
の要素型であるstd::pair
を直接書きたいところではあるが、言語仕様の制限により、マクロの引数の中でカンマを使用することができない。
そこで、要素型は事前にtypedef
しておく必要がある。
#include <iostream>
#include <map>
#include <string>
#include <boost/foreach.hpp>
int main()
{
std::map<int, std::string> m;
m[3] = "a";
m[1] = "b";
m[4] = "c";
typedef std::map<int, std::string>::const_reference type;
BOOST_FOREACH (type x, m) {
std::cout << x.first << ',' << x.second << std::endl;
}
}
実行結果:
1,b
3,a
4,c
しかし多くの場合、std::map
をループする際には、キーか値、どちらかがとれれば十分である。
その場合は、Boost Range Libraryのboost::adaptors::map_keys
を使用してキーのみを抽出、boost::adaptors::map_values
を使用して値のみを抽出できる。
#include <iostream>
#include <map>
#include <string>
#include <boost/range/adaptor/map.hpp>
#include <boost/foreach.hpp>
int main()
{
std::map<int, std::string> m;
m[3] = "a";
m[1] = "b";
m[4] = "c";
// キーのみを抽出
BOOST_FOREACH (int key, m | boost::adaptors::map_keys) {
std::cout << key << ' ';
}
std::cout << std::endl;
// 値のみを抽出
BOOST_FOREACH (const std::string& value, m | boost::adaptors::map_values) {
std::cout << value << ' ';
}
}
実行結果:
1 3 4
b a c
逆順にループするには、BOOST_REVERSE_FOREACH
マクロを使用するか、もしくはシーケンスに対してBoost Range Libraryのboost::adaptors::reversed
を適用する。
BOOST_REVERSE_FOREACH
マクロを使用する場合:
#include <iostream>
#include <vector>
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
int main()
{
const std::vector<int> v = boost::assign::list_of(3)(1)(4);
BOOST_REVERSE_FOREACH (int x, v) {
std::cout << x << std::endl;
}
}
実行結果:
4
1
3
boost::adaptors::reversed
を使用する場合:
#include <iostream>
#include <vector>
#include <boost/assign/list_of.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/foreach.hpp>
int main()
{
const std::vector<int> v = boost::assign::list_of(3)(1)(4);
BOOST_FOREACH (int x, v | boost::adaptors::reversed) {
std::cout << x << std::endl;
}
}
実行結果:
4
1
3
多次元配列のような「シーケンスのシーケンス」に対しては、BOOST_FOREACH
を重ねて使用することで対処できる。
言語組込の配列を使用する場合:
#include <iostream>
#include <boost/foreach.hpp>
int main()
{
typedef int int_a2[2];
const int_a2 a[2] = {
{1, 2},
{3, 4},
};
BOOST_FOREACH(const int_a2 &x, a)
{
BOOST_FOREACH(int y, x)
{
std::cout << y << ' ';
}
std::cout << std::endl;
}
}
「配列への参照型」の記法は直感的でないため、typedef
を用いている。
実行結果:
1 2
3 4
std::vector
を使用する場合:
#include <iostream>
#include <vector>
#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>
int main()
{
std::vector<std::vector<int> > v;
v.push_back(boost::assign::list_of(1)(10));
v.push_back(boost::assign::list_of(2)(20));
BOOST_FOREACH(std::vector<int> const &x, v)
{
BOOST_FOREACH(int y, x)
{
std::cout << y << ' ';
}
std::cout << '\n';
}
}
実行結果:
1 10
2 20
C++11から、BOOST_FOREACH
マクロ相当の言語機能である「範囲for
文(range-based for statement)」が導入された。この2つには、大きく以下の差異がある:
BOOST_FOREACH
マクロは、イテレータの組(std::pair<begin-iter, end-iter>
)をサポートしている。- 範囲
for
文は、要素の変数を、範囲for
文で定義しなければならない。
以下に、その詳細を記載する。
1. イテレータの組
BOOST_FOREACH
マクロは、イテレータの組をループ対象にできる。
C++11 範囲for
文は、ループ対象の型がbegin()
/end()
メンバ関数、もしくはbegin()
/end()非メンバ関数を持っている必要がある。
#include <iostream>
#include <vector>
#include <utility>
#include <boost/foreach.hpp>
int main()
{
const std::vector<int> v = {3, 1, 4};
// pairのfirstを先頭イテレータ、secondを終端イテレータと見なす
const auto range = std::make_pair(v.cbegin(), v.cend());
BOOST_FOREACH (int x, range) {
std::cout << x << std::endl;
}
}
実行結果:
3
1
4
2. 範囲for
文は、要素の変数を、範囲for
文で定義しなければならない。
範囲for
文の構文規則は、以下のようになっている:
for ( for-range-declaration : expression ) statement
for-range-declaration
の部分が、要素の変数宣言を要求している。そのため、以下のようなコードは書けない:
std::vector<int> v;
int x;
for (x : v) {} // コンパイルエラー:変数xは範囲for文で定義しなければならない
// 正しいコード:
for (int x : v) {}
BOOST_FOREACH
マクロには、このような制限はないため、定義済みの変数を要素として使用できる。