実は、テンプレートについては良く知らない。
関数オブジェクトを使ってみた。その記録(わかっている人には当たり前のこと。某氏よりの「プログラムするならその言語の仕様書には目を通せ」という言葉が突き刺さる)。関数オブジェクトを使おうと思ったのは、とある多重ループ(ちょっと複雑)があって、その最深部の処理だけを変えた二つのルーチンを作りたかったからである。C な書き方なら、関数へのポインタを渡して、ループの最深部でそれを呼べばいいのだが、ここはテンプレートを使ってみようと。
テンプレートだと、ループ部分が実際に2回分展開されて、ポインタを渡すよりも微妙に高速化できるかなと。ループの最深部は数千万回は呼ばれるので、少しでも高速化を(ていうのは名目で、テンプレートが使いたかっただけだが)。まず、ループの部分をテンプレートのメソッドにした。簡単に書けば、
template<Class FuncObj> void Class_A::Scanner(FuncObj func) { foreach() { func(x); } }
みたいな。
一つ引っかかったのは、呼び出し側の関数の中で宣言した関数オブジェクトのクラスでは駄目だったこと。
void Class_A::Caller() { Class FuncObj_B { } func_B; Scanner(func_B); // 駄目! }
なので
class Class_A::FuncObj_B { void operator()(int x) { } };
というのを .cpp ファイルの上の方に書いた。だいぶ意味が違うが。
次に引っかかったのは、関数オブジェクトのクラス FuncObj_B は Class_A のメンバ?なのだが、Class_A のメンバ変数にアクセスはできない(static なものを除いて)ようである。クラスとインスタンスの区別というか、Class_A のインスタンスが無くても FuncObj_B のインスタンスが作れるってことだろうか。Class_A のインスタンスからしか FuncObj_B のインスタンスが作れないようにすれば、何とかなるような気もするが。ループ内で普遍なものは、FuncObj_B のインスタンス構築時に引数渡し、ループ毎に変化するものは、operator() の引数渡しで対応。
後、気が付いたのが、
Scanner<FuncObj_B>(func_B)
とか書かなくてもいいこと。自明な場合は<>は省略可? 確かに、swap()とかでも<>は書かないな。当たり前のことかな。
とにかく、テンプレートってのは、言われてみればその理由がわかるのだが、言われないとなぜそういう風になっているのか良くわからない所が多い。今回の話も、一応所望の動作を得ることができたのはいいのだが、もう少し突き詰める必要がありそうだ。