std::unary_functionの存在意義がよくわからなくなった。
前回のエントリで
ただ、この例のstd::unary_functionとかstd::binary_functionとかは本当にvirtualデストラクタがない設計でいいの?という点はちょっと疑問でした。
例えばstd::unary_functionの派生クラスとして、「環境への参照を持つ関数(イメージとしてはクロージャ)」を作ってこいつはデストラクタで何かをしないとダメだとして、unary_functionオブジェクトとしてコンテナにつっこんで何かした後に、オブジェクトをunary_functionとして破壊すると不定な動作になるというのはちょっと不安に思います。
と思って、とりあえずstd::unary_functionとかをいじってみれば理由がわかるかなと思っていじってみた。
練習問題として、一引数関数を受け取ってその導関数を返すような高階関数を書いてみる。
とりあえず一引数関数の表現はstd::unary_functionでよかろうとか思って書き始めて、
template<T>
unary_function<T, T> deriv(unary_function<T, T> f, T dx);
みたいになシグネチャでやろうかと思ったのだけど、よくよく見ると unary_function 自体には operator() が宣言されていないのでこれでは駄目。unary_functionはこういう目的に使うものではないらしい。
functionalヘッダを色々眺めていたら、pointer_to_unary_functionなるクラスがあり、こいつはunary_functionの派生クラスであり、operator() を実装するファンクタクラスぽかった。じゃあ導関数deriveredクラスはpointer_to_unary_functionの派生クラスであり、pointer_to_unary_functionオブジェクトをメンバとして持つ感じにして、
template<class T> class derivered : public pointer_to_unary_function<T, T> { public: derivered(/*...*/) : /*...*/ {} T operator()(T x) const { return ( f(x + dx) - f(x) ) / dx ); } private: pointer_to_unary_function<T, T> f; T dx; };
みたいに行けるかと思ったら、pointer_to_unary_functionのoperator()はvirtualじゃないので継承は無理っぽい。うーん。導関数は一引数関数のサブクラスにしたい気がするけどなあ。まあそれにvirtualデストラクタがないから危険が危ない。
じゃあ抽象クラスfunctorみたいなのを作って、deriveredクラスはfunctorの派生クラスであり、functorオブジェクトを受け取ってderiveredオブジェクトを返すようなderiv関数でも作るかということにしようとしたら、C++お決まりの、functorをポインタで受け取るかconst参照で受け取るかというところでめんどくさくなった。まあ多分ポインタで受け取るのが正しいのだろう。あとderiveredは関数ポインタをメンバに持つべきか、functorをメンバに持つべきかとかも悩ましい。templateで書いちゃってもいいけどとかなんとか悩みながら、やっぱり高階関数書くときはGCが欲しいという結論に達した。
#include<functional> #include<iostream> #include<cmath> using std::unary_function; using std::pointer_to_unary_function; template<class T, class Func> class derivered : public unary_function<T, T> { public: derivered(const Func& f, T dx) : f(f), dx(dx) {} T operator()(T x) const { return ( f(x + dx) - f(x) ) / dx; } private: Func f; T dx; }; template<class T, class Func> derivered<T, Func> deriv(Func f, T dx){ return derivered<T, Func>(f, dx); } using std::ptr_fun; using std::cout; using std::endl; int main() { const double PI(3.141592); pointer_to_unary_function<double, double> f = ptr_fun(sin); cout << f(PI) << endl; cout << deriv(f, 0.0001)(PI) << endl; cout << deriv(cos, 0.0001)(PI) << endl; return 0; }
そんで悩みながら書いたのがこんなんだけど、駄目駄目っぽい感じ。
書いてて思ったのは、おそらくfunctorをポリモルフィックにしようとすると、結局ポインタ経由で使いたくなって、そうすると関数ぽく呼び出すときにデリファレンスしてやらなきゃいけなくなる。でもそれは現在の関数ポインタの挙動と不整合なので(デリファレンスしなきゃいけない関数ポインタなんてANSI以前のCみたいだ!)基本そういう使い方をしないように、unary_functionもpointer_to_unary_functionも仮想関数なしの設計なのかなと思った。でもそうするとunary_functionの存在意義が全く分からない。