いわゆるPerlやRubyやC#でよく使われるforeachがQtでは再現されている。この内部ではどんなことをやってるのか? ちょっと気になったので調べてみた。
Visual Studio 2008で調査・・・ gcc版は簡単そうだな(´∀`;)
1.ループを管理するオブジェクトを作成
2070|template <typename T> 2071|class QForeachContainer : public QForeachContainerBase { 2072|public: 2073| inline QForeachContainer(const T& t): c(t), brk(0), i(c.begin()), e(c.end()){}; 2074| const T c; 2075| mutable int brk; 2076| mutable typename T::const_iterator i, e; 2077| inline bool condition() const { return (!brk++ && i != e); } 2078|};
必要になるコンテナをコピーしてるけど、暗黙の共有が行われてるから問題ないんだろう。たぶん。
2.オブジェクトを参照で受け取る
const QForeachContainerBase &_container_ = qForeachContainerNew(container)
この時点でcontainerの型(=型T)が判明してないので、親クラスへの参照としてオブジェクトを受け取る。containerの型を調べる方法が用意されてれば、この後の処理がずっと楽になるはずなのに・・・
3.型Tを無理矢理認識させる
2080|template <typename T> inline T *qForeachPointer(const T &) { return 0; }
参照を受けてポインタを返すだけの関数。しかも、返してるのはNULLポインタ。つまり、型Tをコンパイラに知らせるためだけに使われる関数っぽい。
4.型Tを他の関数に渡す
true ? 0 : qForeachPointer(container)
こんな形の三項演算子がいくつも出てくるけど・・・ これも、考え方は2と同じだな。
5.型Tを使ってポインタを変換
3と4のテクニックを使って無理矢理変換。
qForeachContainer(&_container_, true ? 0 : qForeachPointer(container))
変換に使ってる関数はこんな感じ。
2085|template <typename T> 2086|inline const QForeachContainer<T> *qForeachContainer(const QForeachContainerBase *base, const T *) 2087|{ return static_cast<const QForeachContainer<T> *>(base); }
親クラスへのポインタを型Tのポインタに変換。これでようやく、ループを管理するオブジェクトがまともに使えるように・・・ 面倒だなぁ。
6.2重のループで本文を回す
ここまでの解釈を使ってforeachのマクロを単純化するとこんな感じか?
1|QStringList args = app.arguments(); 2| 3|for( QFCon con = QFCon( args ); con.condition(); ++con.i ) { 4| for( QString str = *con.i; con.brk; con.brk-- ) { 5| ループの本文; 6| } 7|}
QFConがループを管理するオブジェクト。1で出てきたQForeachContainerに相当する。内側のループは確実に1周で抜ける・・・ 何か、深い意味があるんだろう。たぶん。
condition()の中でbrkも判定に使ってるけど・・・ これは要らないような気が。気のせいかな。
ちなみに、手元の環境では3と4をすっ飛ばしてもコンパイルが通ったが・・・
qForeachContainer(&_container_, &container)->condition();
こんな感じで。これだと無理な環境があるんだろう。たぶん。というか、どうせコンテナクラスしかforeachで使えないんだから、コンテナ側にforeachのネタを仕込んでおくべきか?
参考になったのはこのあたり。
http://doc.trolltech.com/4.5/containers.html#the-foreach-keyword
できるだけまじめにC++を使いながら、それでも速度は犠牲にしたくないという涙ぐましい努力が垣間見える結果に。ちょっとしたループの展開なら、これを使ってあげるべきなのかな。
(´-`).。о〇(そもそも、速度を気にするようなプログラムなんて、最近書いてないしな・・・)