C++が末尾再帰の最適化を行えないわけ
ふと思いついたので、ちょっとした小話。
まずは、末尾再帰版sum。
#includeint sum(int i, int s) { if (i == 0) return s; else return sum(i-1, s+i); } int main() { std::cout << sum(1000000, 0); }
gccでもclでもちゃんと実行できる。が、1行入れるだけで破綻する。
int sum(int i, int s) { std::string str(""); if (i == 0) return s; else return sum(i-1, s+i); }
なんでだろう。つまり、stringのデストラクタ呼び出しが控えてるってことだ。呼び出し順を保存するためには、sumの呼び出しの前に破壊するわけにはいかない。strがスコープ抜ける前にデストラクタで一仕事待ってるので、困ったことになる。っていうか、デストラクタで仕事すべきではないんじゃなかったか。この辺、いつ破壊されるかわからないよーん、と開き直ってGCしてれば最適化できるわけだ。ホントかな? たぶん。
class C { public: ~C() {} }; int sum(int i, int s) { C c; if (i == 0) return s; else return sum(i-1, s+i); }
意外とこれだけでgccはダメ。デストラクタを書かなかったら大丈夫ではあった。clはまだがんばれる。あ、gcc4なら-O3にすれば通るようだ。
NRVOの件もそうだったけど、なんでもできすぎると最適化がかけづらいよねぇ、っていうはなし。あぁ、最近研究室内でのOCamlの肩身が狭い。