C++11のrange-based for文について
先月、アスキードワンゴから江添亮さんの「C++11/14コア言語」が発売されました。C++でコードを書いているくせにC++の言語仕様を真面目に勉強していなかったので、この本を読んでC++力の向上に努めています。今まで知らなかった細かいことがたくさん書いてありとても勉強になります。

- 作者: 江添亮
- 出版社/メーカー: KADOKAWA
- 発売日: 2015/09/25
- メディア: 単行本
- この商品を含むブログ (2件) を見る
ちなみにほとんど同内容の「C++11の文法と機能」はGNU Free Documentation Licenseで公開されています。
6章にあったrange-based for文というC++11から導入された機能が便利そうだったので使い方をメモしておきます。
基本の使い方
本によれば「range-based for文はレンジをサポートしている配列、初期化リスト、クラスの各要素に対して、それぞれ文を実行するための文である。」とのことです。
書式は
for ( for-range-宣言: for-tange-初期化子 ) 文
という感じです。配列名を指定するだけでループのための変数を用意せずに配列全体に対して処理を行うことができます。他言語のforeach文やPythonのfor文に近いイメージで使えそうです。簡単な例を示します。
#include <iostream> using namespace std; int main() { int a[3] = {1, 2, 3}; // よくある書き方 for (int i=0; i<3; ++i) cout << a[i] << " "; cout << endl; // range-based for文を使った書き方 // 配列の各要素をコピーで受ける for (int i : a) cout << i << " "; cout << endl; // 配列の各要素を参照で受ける for (int &ref : a) cout << ref << " "; cout << endl; // auto指定子を使う for (auto i : a) cout << i << " "; cout << endl; // auto指定子を使う for (auto &ref : a) cout << ref << " "; cout << endl; }
出力
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
なお、C++11の機能を使っているので通常はコンパイル時にオプションを指定する必要があります。
例
g++ main.cpp -std=c++11 clang++ main.cpp -std=c++11
参照を使った場合は配列の中身の書き換えができます。また、参照に対してconstを使うこともできます。
#include <iostream> using namespace std; int main() { int a[] = {1, 2, 3}; // コピーに対する代入 for (auto i : a) i *= 2; // 変更されない for (auto i : a) cout << i << " "; cout << endl; // 参照に対する代入 for (auto &ref : a) ref *= 2; // 変更される for (auto &ref : a) cout << ref << " "; cout << endl; // const指定をすると代入に対してコンパイルエラーを吐く // for (const auto &ref : a) ref *= 2; }
出力
1 2 3 2 4 6
コピーを使うとコピーのための処理が必要ですが、参照にするとコピーの分のコストが抑えられるようです。
range-based for文は配列以外についても使うことが出来ます
#include <iostream> #include <string> #include <vector> #include <map> #include <iterator> // 本には必要とあったが、g++/clang++ではインクルードしなくてもコンパイルできた using namespace std; int main() { // 初期化リストに対する処理 for (auto i : {1, 2, 3}) cout << i << " "; cout << endl; // vectorに対するループ vector<int> v = {1, 2, 3}; for (auto i : v) cout << i << " "; cout << endl; // mapの作成 map<string, int> m; m["shino"] = 155; m["alice"] = 139; m["ayaya"] = 159; m["youko"] = 163; m["karen"] = 150; // mapに対するループ for (const auto i : m) { cout << i.first << " " << i.second << endl; } // 2次元配列の作成 int a[4][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12} }; // 2次元配列に対するループ for (auto & row : a){ for (auto & i : row){ cout << i << " "; } cout << endl; } // 以下のコードはエラー /*for (auto row : a){ // rowには&a[0][0],...,&a[3][0]の値がコピーされる for (auto i : row){ // rowに対するbegin()が存在しないのでエラー cout << i << " "; } cout << endl; }*/ }
出力
1 2 3 1 2 3 alice 139 ayaya 159 karen 150 shino 155 youko 163 1 2 3 4 5 6 7 8 9 10 11 12
以上です。ループのための余計な変数を使わずに済み、コードが簡潔になりそうなので活用していきたいと思います。