9

std::forward変数で使用するauto&&ことが、それらの変数を渡して移動を許可する正しい方法であるかどうかを理解しようとしています。

関数があると仮定します:

void moveWidget(Widget&& w);

そして呼び出し元 - 右辺値と左辺値を参照する 2 つの変数:

Widget w;
auto&& uniRefLV = w;            // lvalue initialiser, 
                                // uniRefLV's type is Widget&

auto&& uniRefRV = std::move(w); // rvalue initialiser, 
                                // uniRefRV's type is Widget&&

型推論が行われているため、型の変数auto&&ユニバーサル参照であることはわかっています。これは、uniRefRVとの両方uniRefLV普遍的な参照であることを意味します。

私の例では、右辺値左辺値であることは明らかですuniRefRVが、概念的にはどちらも普遍的な参照であり、定義が異なる場合、右辺値または左辺値のいずれかを表すことができます。uniRefLV

moveWidget()ここで、これらのユニバーサル参照型を呼び出して完全転送したいと思います。ガイドライン (Scott Meyers による) は次のように述べています。

を介して右辺値参照を渡し、を介してstd::moveユニバーサル参照を返しstd::forwardます。

そして、私がガイドラインを完全に誤解していない限り、それを使用するのは理にかなっているように思えますstd::forward。しかし、考えられるすべての選択肢を考えてみましょう。

// (1) std::move:
moveWidget(std::move(uniRefLV)); // Compiles and looks fine
                                 // but violates the guideline?
                                 // (unconditionally casts lvalue to rvalue)

moveWidget(std::move(uniRefRV)); // Same as above - but not an issue here
                                 // as we cast rvalue to rvalue

// (2) std::forward with Widget:
moveWidget(std::forward<Widget>(uniRefLV)); // Compiles, follows the guideline
                                            // but doesn't look right - what if
                                            // we didn't know Widget's type?

moveWidget(std::forward<Widget>(uniRefRV)); // Same as above

// (3) std::forward with decltype:
moveWidget(std::forward<decltype(uniRefLV)>(uniRefLV)); // Fails to compile! (VC10)
                                                        // follows the guideline
                                                        // has nice and short syntax :)

moveWidget(std::forward<decltype(uniRefRV)>(uniRefRV)); // Compiles fine

uniRefLV両方の参照を同等に扱うべきだと思いますuniRefRVか? また、完全な転送のために 3 つのオプションのどれを使用する必要がありますか?

4

4 に答える 4

11

ガイドラインを誤解しています。または、少なくとも文字通りに受け取りすぎます。

ここには、3 つの重要な点があると思います。

まず、ここではすべてのタイプが知られています。ユニバーサル参照に関するアドバイスは、主に、左辺値参照または右辺値参照であるかどうかがまったくわからないテンプレートを使用した汎用コードに適用されます。

次に、関数は右辺値参照を受け取ります。右辺値を渡す必要があります。限目。ここでは選択の余地はありません。

そして、論理的な結論は、ユニバーサル参照を渡したくないということです。渡すものは何でも右辺値でなければならず、左辺値になることはできません。ユニバーサル参照は左辺値にすることができます (左辺値参照としてインスタンス化されている場合)。ユニバーサル参照を渡すということは、「これがどのような参照なのかわかりませんが、右辺値または左辺値として渡すことができるので、取得したとおりに正確に渡すことを意味します」。この質問のケースは、「何を渡さなければならないかを正確に知っているので、それを渡すことになります」のようなものです。

于 2013-07-02T15:40:11.597 に答える
4

ここで少し混乱したと思います。「ユニバーサルリファレンス」uniRefLVでもありません。uniRefRVそれらは単なる変数であり、どちらも明確な型を持っています。どちらの型もたまたま参照型ですが、それは重要ではありません。

を呼び出すには、どちらの場合もmoveWidgetを使用する必要があります。どちらの場合も、セマンティクスは、移動後にオリジナルが移動されたため、明確な状態ではなくなるというものです。(それでできる最善の方法は、それを破壊するか、再割り当てすることです。)std::movew

対照的に、真のユニバーサル参照テンプレート引数であり、常に標準的な形式でなければなりません:

template <typename T> void foo(T &&);
//                            ^^^^^^

foo(bar());
foo(x);
foo(std::move(y));

あれは、

  • テンプレートは関数テンプレートです。
  • テンプレート パラメータTは関数パラメータとして発生しT &&
  • 現実化された特殊化のテンプレート引数が推定されます。

これらの条件が満たされると、T &&がユニバーサル参照になりstd::forward<T>、正しい参照型を保持する を介してそれを渡します。

于 2013-07-05T21:08:23.107 に答える