インライン展開と仮想関数

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だった。多分、インライン展開したあげく、結果を使わない+副作用がないから全部削っちゃったんだろうな。