2014年9月23日火曜日

setter関数について (実装別コピー回数)

C++では、setter関数を書くとき、
  1. void setter(type val) { var = val; }
  2. void setter(const type& val) { var = val; }
  3. void setter(const type& val) { var = val; }
    void setter(type&& val) { var = std::move(val); }
  4. template<typename T>
    void setter(T&& val)
    { var = std::forward<T>(val); }
  5. void setter(type val) { var = std::move(val); }
と、様々な書き型ができます。3〜5はC++11から使えるようになりました。

結論から書くと、
  • intやfloatなら1で
  • std::stringなどmoveが軽く、コピーできるなら5で
  • 派生クラスなどコピーできないなら、2で
  • 自前クラスでmoveが重く、コピーもできて、
    どうしても高速化したいなら、3で
という感じで使い分けるのがいいんじゃないかな?
4は3を1行で書いてるだけなので、普段は使いにくいかと。

3で全部書くから5は使わないという方針もありですが、
同じ関数を何個も書くのは資源の無駄ですし、
5を取り入れるのはいい考えだと思います。

最後に、それぞれの処理内容を比べてみましょう。
  1. lvalueだと関数呼び出し時のコピー1回、代入時のコピー1回
    rvalueだと関数呼び出し時のコピーは省略、代入時のコピー1回
  2. lvalueだと関数呼び出しは参照渡し、代入時のコピー1回
    rvalueだと関数呼び出し時は参照渡し、代入時のコピー1回
  3. lvalueだと関数呼び出しは参照渡し、代入時のコピー1回
    rvalueだと関数呼び出しは参照渡し、代入時のムーブ1回
  4. lvalueだと関数呼び出しは参照渡し、代入時のコピー1回
    rvalueだと関数呼び出しは参照渡し、代入時のムーブ1回
  5. lvalueだと関数呼び出し時のコピー1回、代入時のムーブ1回
    rvalueだと関数呼び出し時のコピーは省略、代入時のムーブ1回
ここでは、rvalueそのものを作成するコンストラクタ、例えば、
“setName(std::string("str"));”のstd::stringのコンストラクタ部分は
数えていません。

ムーブができるようになったので、5のコスト低下が顕著です。
また、5の場合内部でメモリ確保をしないので、noexceptionが
つけれます。そんな感じで5はなかなかいい選択肢になってます。

0 件のコメント:

コメントを投稿