C++03/11における関数の引数型とコピー/ムーブ処理コストとの関係について。
本記事の内容は C++Now 2012 Keynote: "Moving Forward with C++11" スライド資料(Part I, Part II) に基づく。(Part IIのpp.22-57)
型Tに対する変更操作を行う関数において、引数の型をconst参照渡し(const T&
)*1または値渡し(T
)とするどちらが “良い” デザインかという話。ここでは関数呼び出しから値を返すまでに生じるコピーコンストラクタ/ムーブコンストラクタの呼び出し回数によって評価する。つまり、回数が少ない方が低コスト=良いデザインという観点にたつ。
// const参照渡し; pass-by-const-reference T modify1(const T& x) { T tmp(x); tmp.modify(); return tmp; } // 値渡し; pass-by-value T modify2(T x) { x.modify(); return x; }
なおC++標準規格が許容する戻り値最適化(RVO; Return Value Optimization)/名前付き戻り値最適化(NRVO; Named Return Value Optimization)*2、コンパイラによって常に行われるものと仮定する。
要約:
- C++03:通常は
const T&
でよい。ただし型のサイズが小さくtrivialな場合はT
の方が低コスト。(int
などの組込み型ではconst int&
よりも単にint
の方が低コスト。) - C++11:コピー操作よりも低コストなムーブ操作があり、かつ実引数が常にlvalueという使われ方でなければ、
T
による値渡しが有効。 - C++11:const lvalue参照渡し(
const T&
)+rvalue参照渡し(T&&
)の2種類をオーバーロード関数として提供すると、ムーブまたはコピー 1回 まで削減される。 - 「常に値渡しを使うこと」や「値渡しは禁止」は共に正確でなく、関数インタフェース設計の問題である。
C++03
lvalue | rvalue | |
const T& | 1 copy | 1 copy |
T | 2 copy | 1 copy |
T modify1(const T& x) { T tmp(x); // copy発生 tmp.modify(); return tmp; // (NRVO) } T modify2(T x) // lvalueのときcopy発生 { x.modify(); return x; // copy発生 } T t0; modify1(t0); // lvalue modify1(T()); // rvalue
C++11
lvalue | xvalue | prvalue | |
const T& | 1 copy | 1 copy | 1 copy |
T | 1 copy 1 move |
2 move | 1 move |
T modify1(const T& x) { T tmp(x); // copy発生 tmp.modify(); return tmp; // (NRVO) } T modify2(T x) // lvalueのときcopy発生、xvalueのときmove発生 { x.modify(); return x; // move発生 } T t0; modify1(t0); // lvalue modify1(std::move(t0)); // xvalue modify1(T()); // prvalue
C++11: 関数オーバーロード
lvalue | xvalue | prvalue | |
const T& | 1 copy | 1 copy | 1 copy |
T | 1 copy 1 move |
2 move | 1 move |
const T& T&& |
1 copy | 1 move | 1 move |
// [A] const lvalue参照(const T&) T modify3(const T& x) { T tmp(x); // copy発生 tmp.modify(); return tmp; // (NRVO) } // [B] rvalue参照(T&&) T modify3(T&& x) { x.modify(); return std::move(x); // move発生 } T t0; modify3(t0); // lvalue → [A] modify3(std::move(t0)); // xvalue → [B] modify3(T()); // prvalue → [B]