戻り値最適化

More Effective C++を入手した。まだ4章までしか読んでないけどやっぱこの手の本は勉強になるな〜。

とりあえず、今日覚えたこと。

戻り値最適化を促進する為には無名一時オブジェクトを返せ。

例えば次のコードでは

point a = b + c;
  1. operator+(b, c)を実行して内部で b+c の結果を保持する一時オブジェクトを生成
  2. それを a.operator=( ... ) で代入
  3. 一時オブジェクトを破棄

という手順をとることになり、無駄が生じる。
そこで、「aのメモリ内に直接 b+c の結果を生成しちゃう」ということをしてくれるのが戻り値最適化というものらしい。

具体的に次の3つの実装で実験してみた。

class point
{
  // ...
  point& operator+=(const point& rhs);
};

// 素直な実装
const point add1(const point& lhs, const point& rhs){
  point tmp(lhs);
  return lhs += rhs;
}

// 値渡しでlhsをコピー
const point add2(point lhs, const point& rhs){
  return lhs += rhs;
}

// 無名一時オブジェクトを返す
const point add3(const point& lhs, const point& rhs){
  return point(lhs) += rhs;
}
  • 1つ目は非常に素直な実装。ちなみにboost::operatorsの実装もこれだった。
  • 2つ目はC++ Coding Standards等で紹介されていた実装。
  • 3つ目が本書で推奨されていた実装。

結論から言うと、私の環境(gcc 4.1.2)ではどれも全く同じ。*1
この最適化は当り前のものらしい。だけど、必ず最適化されると前提してコードを書くのも嫌だし積極的に無名一時オブジェクトを使って最適化を促進したほうが良いのだろうな。

考えてみれば無名オブジェクトはほとんどの場合すぐに、他のオブジェクトに代入されたり、他のオブジェクトの初期化に使われたりするから(一時オブジェクトだからアドレスの取得はできないので、他のオブジェクトに代入するしかない)、ほとんどの場合に除去しちゃって問題ないわけだし無名にしたほうが良いってのは納得できる。

*1:たぶん。というかgcc -Sのコードが汚すぎて読む気力がないorz