7

次のコードはVC2010を失敗させます:

//code1
std::string& test1(std::string&& x){
  return x;
}
std::string str("xxx");
test1(str);  //#1 You cannot bind an lvalue to an rvalue reference

//code2 
std::string&& test1(std::string&& x){
  return x;  //#2 You cannot bind an lvalue to an rvalue reference
}

#1を説明する記事がいくつかありますが、#2も失敗する理由がわかりません。

std::moveがどのように実装されるか見てみましょう

template<class _Ty> inline
    typename tr1::_Remove_reference<_Ty>::_Type&&
        move(_Ty&& _Arg)
    {   // forward _Arg as movable
    return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
    }
  1. moveの引数はまだ右辺値の参照ですが、move(str)は問題ありません。
  2. moveも右辺値を返します。

std:moveの魔法は何ですか?

ありがとう

4

5 に答える 5

10

std::moveのパラメータは右辺値参照のように見えますが、混乱しているように見えます-右辺値ではないのにmove(str)、なぜ呼び出すことができますstrか?

ここでの秘訣は、右辺値参照がテンプレートパラメータで混乱を招くように機能することです。

テンプレートパラメータTint、の場合T&&、右辺値参照になりますint&&
ただし、が左辺値参照の場合Tは、左辺値参照int&T&&もなりますint&

これは、方法&&&組み合わせによるものです。

Type & &&   ==  Type &
Type && &   ==  Type &
Type & &    ==  Type &
Type && &&  ==  Type &&

したがって、、を呼び出すとmove(str)、のパラメータタイプも-左辺値参照になります。これにより、関数呼び出しをコンパイルできます。次に、値を右辺値参照にキャストするだけです。Tstd::string&move<std::string&>std::string&move

于 2012-08-30T09:25:47.040 に答える
4

std::move単なるキャストと考えることができます(ただし、型キャストではなく、キャスト)。式std::move(x)はと同じ値の右辺値ですが、それ自体が左辺値xであっても機能します。x

あなたの例では、「code2」xは確かに左辺値です(「文字列への右辺値参照」タイプ)。この左辺値は関数の戻り型(「文字列への右辺値参照」)にバインドできないため、右辺値式に明示的にキャストする必要があります。

moveまた、私が通常呼び出す、の反対を作成することもできますstay。これは、右辺値を左辺値に変換します(注意して使用してください)。

template <typename T> T & stay(T && t) { return t; }

これは主に、ひねくれたワンライナーや、バーで女の子を感動させるのに役立ちます。

于 2012-08-30T09:07:06.713 に答える
3

ここの人たちはすでに質問に答えていますが、もっとはっきりと言う必要があると思います。人々が右辺値参照と混同されることが多い理由は、次の規則にあります。

名前付き右辺値参照左辺値です。

最初は紛らわしいですが、このルールは理にかなっています。右辺値参照の目的は、不要になったオブジェクトにバインドすることです。一時的なものか、不要になることがわかっているものにバインドしますが、コンパイラーはそれを理解できません。

名前付き右辺値参照は、何度も参照できるものです。

std::unique_ptr<int> && rref = std::unique_ptr<int>{ new int{1} };
std::unique_ptr<int> p2{rref};  // if it worked...
rref->use();                    // this would crash 

ここでは、最初の行に一時オブジェクトを作成しましたが、右辺値参照にバインドされているため、ほぼ自動オブジェクトのように機能します。複数回アクセスできます。そして、最後の行が機能するためには、2番目の行がコンパイルされてはなりません。

std::move名前付き右辺値参照(左辺値)を名前なし右辺値参照(右辺値)に変更するにはどうすればよいですか。

于 2012-08-30T10:03:40.003 に答える
0

#1を説明する記事がいくつかありますが、#2も失敗する理由がわかりません。

名前がある場合は、左辺値であることを忘れないでください。2番目のケースでは、xは右辺値参照であっても、左辺値のままです。左辺値右辺値に変換する機能があります。結果の式は、右辺値参照を要求する任意のコードで受け入れることができる右辺値です。(a )std::movestatic_cast<Type&&>(yourVar)Type &&

いくつかの例で説明します。元の例では、次を置き換えます。

std::string str("xxx");
test1(str);

test1(string("xxx"));

そこで、文字列オブジェクトには名前がなくなり、右辺値になり、test1によって受け入れられます。

移動はどのように機能しますか?繰り返しますが、非常に簡単です。繰り返しますが、通話を次のように置き換えます。

test1(std::move(str));

またはと

test1(static_cast<std::string&&>(str));

基本的に同じことですが、std::moveが意図をよりよく説明しています。

要点

  • 名前がある場合は左辺値です

  • 名前がない場合、それは右辺値であり、Type &&右辺値参照)が要求される場所であればどこでも受け入れることができます。

  • 左辺値から右辺static_cast値にすることができます(ただし、代わりにそれを使用してください)が、それを行うときは、自分が何をしているのかを知る必要があります。(つまり、移動セマンティクスが実装されている型で使用します。ここでは詳しく説明しません。移動セマンティクスのトピックに関するすばらしい記事にリンクするだけです)std::move

于 2012-08-30T09:50:41.347 に答える
0

移動セマンティクスの魔法は、破壊可能性を利用することです。

std :: stringのように動的に割り当てられたバッファーの存続期間を管理するクラスを想像してみてください。コピーコンストラクターstring const&は、引数が後で実際に必要かどうかを知ることができないため、常にこのバッファーのクローンを作成する必要があります。これはコストのかかる操作になる可能性があります。

引数が使い捨てであることが(のコピーコンストラクターをオーバーロードすることによってstring&&)知ることができれば、コピーする必要のないバッファーを「盗む」ことができます(複雑な式の場合は何度も繰り返します)。

前述のように、右辺値参照は左辺値です(上記の例では、バッファーを盗むためにフルアクセスが可能になりました)。ここで、別の同様にオーバーロードされた呼び出し式で引数を使用したいとします。引数がstd::moveないと、私たちにとって貴重であるように見え、string const&オーバーロードは解決されstd::moveますが、「移動トリック」を繰り返すことができます(引数がコールサイト)。

于 2012-08-30T14:08:40.337 に答える