10

次のようなものがあるとします。

foo(A&& a);

もし、するなら

A a;
foo(a);

コンパイルされず、文句を言うと左辺値をA&&にバインドできません。それは完全に問題ありません。

ただし、std :: moveの署名がある場合、

template<class T> typename remove_reference<T>::type&& std::move(T&& a);

fooの場合と同様に、右辺値参照が必要なようですが、次のコードが準拠しているのはなぜですか?

A a;
std::move(a);

a左辺値ではありませんか?

さらに、コンパイルがインスタンス化されると言われています:

typename remove_reference<A&>::type&& std::move(A& && a);

なぜそうではないのかわかりません:

typename remove_reference<A>::type&& std::move(A && a);

aにはタイプAではなく、タイプのように見えA&ます。

4

3 に答える 3

10

いいえmove、右辺値参照は使用しません。コミュニティによってユニバーサル参照と呼ばれているものを使用します。タイプ推定されるテンプレートパラメータは、参照の折りたたみのルールに従って動作します。これの意味は:

  • の場合、単純にT;になります。KT&&K&&
  • の場合、Tは;に折りたたまれます。K&T&&K&
  • の場合、Tはに折りたたまれます。K&&T&&T&&

&これは、との論理積のようなものです。&&ここで、&は0で、&&は1です。

      &    &&
   |-----------|
&  |  &  |  &  |
   |-----|-----|
&& |  &  | &&  |
   |-----------|

そして、それがmove右辺値と左辺値の両方でどのように機能するかです。

例:

template<typename T>
void f(T&&);

f<int>   // T is int;   plugging int into T makes int&& which is just int&&
f<int&>  // T is int&;  plugging int& into T is int& && which collapse to int&
f<int&&> // T is int&&; plugging int&& into T is int&& && which collapse to int&&

参照の折りたたみは、テンプレートパラメータでのみ発生することに注意してください。int&& &&直接入力してコンパイルすることを期待することはできません。もちろん、そのように手動でタイプを指定することはありません。これらは、参照がどのように崩壊するかを示すためだけのものです。

だからあなたは本当にそれをこのように呼ぶでしょう:

int i;

f(i); // T is int&;  int& && collapses to int&
f(4); // T is int&&; int&& && collapses to int&&

move参照の折りたたみは、aを返さない理由でもあります。左辺値の参照であるT&&場合は参照が折りたたまTれ、左辺値の参照をmove返すだけです。remove_reference非参照型に到達して、実際&&に「右辺値参照」を意味するようにします。

詳細については、http: //isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyersをご覧ください。

于 2013-01-25T23:10:07.857 に答える
3

T&& 型推論のコンテキストでの構文形式(テンプレート引数の推定を含みますが、たとえば、として宣言された変数の型の推定も含みますauto)は、右辺値参照ではなく、ScottMeyersが[ユニバーサル参照]と呼ぶものを示します。非常に特定の構文形式 T&&のみが普遍的な参照を示し、他の同様の形式はそのように見なされないことに注意してください。例えば:

template<typename T> 
void foo(T&& t); <-- T&& is a universal reference

template<typename T> 
void foo(T const&& t); <-- T const&& is NOT a universal reference

template<typename T> 
void foo(S<T>&& t); <-- S<T>&& is NOT a universal reference

template<typename T> 
struct S { void foo(T&& t); }; <-- T&& is NOT a universal reference

ユニバーサル参照は、左辺値と右辺値の両方にバインドできます。型の左辺値Aがバインドされている場合、Tはであると推定され、参照の折りたたみの規則(になる)によりA&、引数の型は(左辺値参照)に解決されます。タイプの右辺値がバインドされている場合、はであると推定され、引数のタイプは(右辺値参照)に解決されます。A&A& &&A&ATAA&&

[参照の折りたたみルールは複雑に見えるかもしれませんが、実際には非常に簡単です。StephanT. Lavavejを引用すると、「左辺の参照は伝染性です」。つまり、フォーム、、、T&& &またはT& &T& &&インスタンス化されると、常にT&フォームにのみ解決されます。T&& &&に解決されT&&ます]

これが、引数が左辺値std::moveである場合に関数テンプレートが次のようにインスタンス化される理由です(と推定されます)。TT&

typename remove_reference<A&>::type&& std::move(A& && a);

一方、引数が右辺値の場合は次のようにインスタンス化されます(Tと推定されますA

typename remove_reference<A>::type&& std::move(A&& a);
于 2013-01-25T23:13:56.163 に答える
1

他の人が言ったことにもかかわらず、標準は右辺値参照についてのみ話します。

これが std::move でどのように機能するかの鍵は、テンプレート引数推定の規則における明示的な特別規則です。

[...] [宣言された関数パラメーター型] が cv 修飾されていないテンプレート パラメーターへの右辺値参照であり、引数が左辺値である場合、型推定のために A の代わりに型「A への左辺値参照」が使用されます。[ ...]

もう 1 つの部分は、参照の崩壊に関するルールです。

[...] 型テンプレート パラメータ [...] が型 T への参照である型 TR を示す場合、型「cv TR への左辺値参照」を作成しようとすると、型「T への左辺値参照」が作成されます。 」、型「cv TR への右辺値参照」を作成しようとすると、型 TR が作成されます。

関数パラメーターでtemplate<class T> typename remove_reference<T>::type&& std::move(T&& a);は、上記のルール (「cv 非修飾テンプレート パラメーターへの右辺値参照」) に一致するため、引数が左辺値の場合、推定される型は引数の型への左辺値参照になります。あなたの場合、それは T = A& につながります。

それを move の宣言に代入すると、

remove_reference<A&>::type&& std::move<A&>(A& && a);

remove_reference の定義と参照の折りたたみ規則 (TR => TR への右辺値参照) を使用すると、次のようになります。

A&& std::move<A&>(A& a);

Scott Meyer の普遍的な参照の概念は、他の回答で提唱されているように、型推定のルールと参照の崩壊の組み合わせのこの驚くべき効果を思い出すのに役立ちます。推定された型への右辺値参照は左辺値参照になる場合があります型は左辺値参照であると推定される場合があります)。しかし、標準には普遍的な参照はありません。スコット・マイヤーズが言うように: それは嘘です - しかし、真実よりも役立つ嘘です...

std::forward はこのテーマの別のひねりであることに注意してください: 追加の間接化を使用して引数の演繹を防止します (そのため、型を明示的に指定する必要があります) が、参照の折りたたみを使用して、左辺値を左辺値として、右辺値を右辺値として転送します。

于 2013-01-25T23:46:26.323 に答える