インライン展開と仮想関数
virtual にするか template にするか悩ましいので両方できるようにしておけばよくね、という結論に達したけど、実際にちゃんと template にしたときインライン展開されるのか気になる。コンパイル時に型が決定していれば大丈夫な気はするんだけど。
#include <iostream> #include <boost/timer.hpp> using namespace std; using namespace boost; class Base { public: virtual int f(int x) = 0; }; class Sub : public Base { public: int f(int x) { return x; } }; int f0(Base& o) { int s; for (int j = 0; j < 10; j++) for (int i = 0; i < 100000000; i++) s += o.f(i); return s; } template<typename T> int f1(T& o) { int s; for (int j = 0; j < 10; j++) for (int i = 0; i < 100000000; i++) s += o.f(i); return s; } template<typename T> int f2(T o) { int s; for (int j = 0; j < 10; j++) for (int i = 0; i < 100000000; i++) s += o.f(i); return s; } template<typename T> int f3(T& o) { int s; for (int j = 0; j < 10; j++) for (int i = 0; i < 100000000; i++) s += o.T::f(i); return s; } int main() { Sub o; timer t; f0(o); cout << t.elapsed() << endl; t.restart(); f1(o); cout << t.elapsed() << endl; t.restart(); f2(o); cout << t.elapsed() << endl; t.restart(); f3(o); cout << t.elapsed() << endl; }
わかりにくいけど、f0 が base class の参照渡し。f1 が sub class の参照渡し。f2 が sub class の値渡し。f3 が sub class の参照渡しで、T::f で関数を明示的に指定。それぞれ、たくさん呼び出したときの実行時間を表示している。
で、結果。
$ ./a 7.281 7.375 1.047 0.969
要は、参照渡しだけだとダメよというはなし。意外なのか意外じゃないのか。値渡しか、呼び出す関数を明示すれば OK。Sub が継承されている可能性があるからなかな。
VC でもほぼ同じ結果。cl でおもしろいのは、最後の2個の実行時間が0だった。多分、インライン展開したあげく、結果を使わない+副作用がないから全部削っちゃったんだろうな。