[C++] イテレータ with ループカウンタ
イテレータで反復処理を行うとき、ループカウンタとりたい時ってあるじゃないですか。vectorならイテレータ同士の引き算で位置が求まりますが、なんかアレな気がしないですか。しないですか。そうですか。でもランダムアクセスイテレータだったらdistance使わなきゃないじゃないですか。いいですか。そうですか。もういっその事別にループカウンタ用の変数置けばいいじゃないですか。でもこれってなんか負けた感じがするんですよね…
ということで今回は反復しつつカウンタの値も取るコードを書いてみました。
とりあえず
int i = 0;
for(std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it)
{
hoge(i);
i++;
}
で問題なさそうならこれでいいと思います!
両方とりたいアナタに
以下にコードを貼ります。
#include <iostream> | |
#include <vector> | |
/* -- ここから -- */ | |
template<typename T, typename C = int> | |
class Element | |
{ | |
template<typename S> | |
friend class Iterator; | |
private: | |
inline void set(const C i, T &v) | |
{ | |
index = i; | |
value = v; | |
} | |
public: | |
C index; | |
T &value; | |
inline Element(const C idx, T &val) : index(idx), value(val) { } | |
}; | |
template<typename T> | |
class Iterator | |
{ | |
typedef typename T::iterator iter_type; | |
typedef Element<typename T::value_type> elem_type; | |
private: | |
int index; | |
iter_type iterator; | |
elem_type elem; | |
public: | |
inline Iterator(iter_type it) : index(0), iterator(it), elem(0, *it) { } | |
inline bool operator!=(const Iterator<T> &it) const { return iterator != it.iterator; } | |
inline elem_type &operator*() { return elem; } | |
inline elem_type *operator->() { return &(operator*());} | |
inline const Iterator &operator++() | |
{ | |
elem.set(++index, *(++iterator)); | |
return *this; | |
} | |
}; | |
template<typename T> | |
class WithIndex | |
{ | |
public: | |
typedef Iterator<T> iterator; | |
private: | |
T &iterable; | |
public: | |
inline WithIndex(T &iterable) : iterable(iterable) { } | |
inline Iterator<T> begin() const { return Iterator<T>(iterable.begin()); } | |
inline Iterator<T> end() const { return Iterator<T>(iterable.end()); } | |
}; | |
template<typename T> | |
inline WithIndex<T> withIndex(T &iterable) { return WithIndex<T>(iterable); } | |
/* -- ここまで -- */ | |
int main(int argc, const char * argv[]) | |
{ | |
std::vector<int> iv = { 2, 4, 6, 8, 10 }; | |
for(auto &e : withIndex(iv)) // インデックスも取る! | |
{ | |
std::cout << e.index << ": " << e.value << std::endl; | |
} | |
return 0; | |
} |
こんな感じ。main関数内はC++11な機能を使ってますが、WithIndexクラス自体はC++03でもコンパイルできます(できるはずです)。range-based forとの相性がよさそうです。普通のforを使う場合は、
typedef WithIndex<std::vector<int> > VectorWithIndex;
VectorWithIndex vi = withIndex(iv);
for(VectorWithIndex::iterator it = vi.begin(); it != vi.end(); ++it)
{
std::cout << it->index << ": " << it->value << std::endl;
}
こんな感じで。うーん、autoがないとちょっと使いづらい。いや、かなり使いづらい。
ちょっとした説明
コード自体はScalaのzipWithIndexを真似てます。こういうシチュエーションってよくあると思うので、ググればきっとたくさんヒットすることでしょう。
イテレータのindexにループカウンタの値が、valueに実際のイテレータの値が代入されます。range-based forならアロー演算子が使われないのでシンプルなコードになるんですが、C++11より前のC++ではイテレータに入っている値を取り出す際にアロー演算子を使うことが多いかと思います。アロー演算子はポインタを返さなければならないため、一度Elementクラスを変数として書き出しています。ちょっとかっこ悪いしなんだかパフォーマンス落ちてそう。
range-based forがとても便利です。beginとendがそれぞれ先頭のイテレータ、末尾のイテレータを返す実装になっていれば利用可能です。autoも使えば反復処理がこんなに簡単に!
0 件のコメント :
コメントを投稿