2012年1月9日月曜日

range based for

使いやすいfor文

リストのすべての要素を捜査するときには、for文を次のように記述します。

// 配列の場合
int array[] = {1, 2, 3, 4};
for (int val : array) {
  std::cout << val << std::endl;
}
// 配列以外の場合
std:vector<int> vec = {1, 2, 3, 4};
for (int val : vec) {
  std::cout << val << std::endl;
}

range based forは、配列と配列以外の場合それぞれ次のように展開され処理されます。

// 配列の場合
int array[] = {1, 2, 3, 4};
for (int i = 0; i < sizeof(array) / sizeof(decltype(*array)); ++i) {
  auto val = *(array + i);
  std::cout << val << std::endl;
}
// 配列以外の場合
std:vector<int> vec = {1, 2, 3, 4};
for (auto i = begin(vec); i != end(vec); ++i) {
  auto val = *i;
  std::cout << val << std::endl;
}

begin/end関数

配列以外のオブジェクトをrange based forに指定した場合、前述したようにbegin/end関数を使用して、最初の要素のイテレータと最後の要素のイテレータを取得しています。このbegin/end関数が何者かというと、<iterator>に次の関数が定義されています。

template<typename C> auto begin(C& c) -> decltype(c.begin());
template<typename C> auto begin(const C& c) -> decltype(c.begin());
template<typename C> auto end(C& c) -> decltype(c.end());
template<typename C> auto end(const C& c) -> decltype(c.end());
template<typename T, size_t N> T* begin(T (&array)[N]);
template<typename T, size_t N> T* end(T (&array)[N]);

上の4つが配列以外のオブジェクト用、下の2つが配列用のbegin/end関数になっています。配列以外のオブジェクト用のbegin/end関数は、宣言を見て分かる通りそのオブジェクトにbegin/endメンバ関数が存在していることが前提となっています。<iterator>をインクルードして、全要素の捜査をしたいクラスにbegin/end関数を定義することで、そのクラスをrange based forで使用することが可能になります。

もし、range based forに使用したいクラスにbegin/end関数がなく、追加することもできない場合は、グローバル関数としてbegin/end関数を定義することで、どのようなクラスであってもrange based forの恩恵をうけることが可能です。

ただし、begin/end関数は、ADLによって呼び出されるため、定義が特定できずにコンパイルエラーになったり、想定外の関数が呼び出されて思わぬ挙動になる場合があるので注意が必要です。

0 件のコメント:

コメントを投稿