[解決済み] 値渡し vs rvalue参照渡し
質問
関数はいつ宣言すればよいのでしょうか。
void foo(Widget w);
とは対照的に
void foo(Widget&& w);
?
これが唯一のオーバーロードであると仮定します(両方ではなく、どちらか一方を選び、他のオーバーロードはない、というように)。テンプレートは関係ありません。関数が
foo
の所有権が必要だとします。
Widget
(例えば
const Widget&
はこの議論に含まれない)。このような状況の範囲外の回答には興味がありません。については、記事末尾の追記をご覧ください。
なぜ
が質問の一部である理由については、投稿の最後にある補遺をご覧ください。
私の同僚と私が思いつく主な違いは、rvalue参照パラメータがコピーについて明示的であることを強制することです。呼び出し側は明示的にコピーを作成する責任があり、それを
std::move
で渡すことになります。値で渡すケースでは、コピーのコストは隠蔽されます。
//If foo is a pass by value function, calling + making a copy:
Widget x{};
foo(x); //Implicit copy
//Not shown: continues to use x locally
//If foo is a pass by rvalue reference function, calling + making a copy:
Widget x{};
//foo(x); //This would be a compiler error
auto copy = x; //Explicit copy
foo(std::move(copy));
//Not shown: continues to use x locally
その違い以外には コピーについて明示することを強制することと、関数を呼び出すときに得られる構文上の糖分を変更すること以外に、これらはどのように違うのでしょうか?インターフェイスについて何か違うことを言っているのでしょうか?これらは互いに効率的なのか、それともそうでないのか?
私と同僚がすでに考えている他のこと。
- rvalue 参照パラメータというのは があります。 を意味し、それを強制するものではありません。呼び出し先で渡した引数が、その後元の状態になる可能性はあります。また、関数が移動コンストラクタを呼び出すことなく引数を食べたり変更したりする可能性もありますが、rvalue 参照であるため、呼び出し元が制御を放棄したと見なすことができます。値で渡す場合、それに移動する場合、移動が起こったと見なさなければなりません。 エリジオンを使用しないと仮定すると、1 つの移動コンストラクター呼び出しは、rvalue によるパスで排除されます。
- コンパイラーは、値渡しでコピー/移動のエリジオンを行うより良い機会があります。この主張を実証できる人はいますか? できれば、標準の行ではなく、gcc/clang から最適化された生成コードを示す gcc.godbolt.org へのリンクがあると望ましいです。これを示そうとした私の試みは、おそらくうまく動作を分離することができなかったのでしょう。 https://godbolt.org/g/4yomtt
追記です。 なぜ 私はこの問題をそんなに制約しているのでしょうか?
- オーバーロードなし - もし他のオーバーロードがあった場合、これは値による渡しと const 参照と rvalue 参照の両方を含むオーバーロードのセットとの議論に発展するでしょうが、その時点でオーバーロードのセットの方が明らかに効率的で勝利します。これはよく知られていることであり、したがって興味深いことではありません。
- テンプレートがない - 転送参照がどのように適合するかには興味がありません。転送参照がある場合は、とにかく std::forward を呼び出します。転送参照でのゴールは、受け取ったものをそのまま渡すことです。コピーは関係なく、lvalueを渡すだけだからです。これはよく知られていることで、面白いことではありません。
-
foo
の所有権を必要とします。Widget
(別名const Widget&
) - 読み取り専用の関数について話しているのではありません。もし関数が読み取り専用であったり、所有したり寿命を延ばしたりする必要がない場合はWidget
を所有または拡張する必要がない場合、答えは自明でconst Widget&
となり、これもまたよく知られていることで、面白みがありません。また、なぜ私たちがオーバーロードについて話したくないのかについても言及します。
どのように解決するのですか?
<ブロッククオートrvalue参照パラメータは、コピーについて明示的にすることを強制します。
そうですね、pass-by-rvalue-referenceは一理ありますね。
<ブロッククオートrvalue referenceパラメータは、引数を動かしてもよいことを意味しますが、それを強制するものではありません。
そう、pass-by-valueは一理ある。
しかし、それはまた、例外保証を処理する機会をpass-by-rvalueに与える:もし
foo
を投げます。
widget
の値は消費される必要はありません。
移動のみの型(例えば
std::unique_ptr
のように、パス・バイ・バリューが一般的なようです(ほとんどは2番目のポイントについてで、1番目のポイントはいずれにせよ適用できません)。
EDIT: 標準ライブラリは私の前の文章と矛盾しており、そのうちの1つは
shared_ptr
のコンストラクタは
std::unique_ptr<T, D>&&
.
コピー/ムーブの両方を持つ型(例えば
std::shared_ptr
のように)、以前の型との整合性をとるか、コピー時に明示的であることを強制するかの選択を迫られます。
不要なコピーがないことを保証したいのでなければ、coherencyにpass-by-valueを使用することになるでしょう。
保証された、あるいは即時のシンクが必要でない限り、私ならpass-by-rvalueを使用します。
既存のコードベースでは、私なら一貫性を保つでしょう。
関連
-
[解決済み】'cout'は型名ではない
-
[解決済み】C++の変数はイニシャライザーを持っているが、不完全な型?
-
[解決済み] 非常に基本的なC++プログラムの問題 - バイナリ式への無効なオペランド
-
[解決済み】c++でstd::vectorを返すための効率的な方法
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み】リンカーエラーです。"リンカ入力ファイルはリンクが行われていないため未使用"、そのファイル内の関数への未定義参照
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み] 非静的データメンバの無効な使用
-
[解決済み】VC++の致命的なエラーLNK1168:書き込みのためにfilename.exeを開くことができません。
-
[解決済み】モダンC++はタダで性能を手に入れられる?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】coutはstdのメンバではない
-
[解決済み】非静的メンバ関数への参照を呼び出す必要がある
-
[解決済み】Visual Studio 2015で「非標準の構文。'&'を使用してメンバーへのポインターを作成します」エラー
-
[解決済み】C++でランダムな2倍数を生成する
-
[解決済み] 既に.objで定義されている-二重包含はない
-
[解決済み】C++プログラムでのコンソールの一時停止
-
[解決済み] 式はクラス型を持つ必要があります。
-
[解決済み] 解決済み] `pthread_create' への未定義の参照 [重複] [重複
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み] コンストラクタや関数に unique_ptr 引数を渡すにはどうしたらいいですか?