2016-08-12 3 views
15

r値が関数間で渡されたときに、C++の動作が不思議でした。この単純なコードで関数内のr値のパラメータ

ルック:私はfoo機能を呼び出すためにmove機能を使用する必要がbar関数内だとき

#include <string> 

void foo(std::string&& str) { 
    // Accept a rvalue of str 
} 

void bar(std::string&& str) { 
    // foo(str);   // Does not compile. Compiler says cannot bind lvalue into rvalue. 
    foo(std::move(str)); // It feels like a re-casting into a r-value? 
} 

int main(int argc, char *argv[]) { 
    bar(std::string("c++_rvalue")); 
    return 0; 
} 

私が知っています。私の質問は今なぜですか?

Iが可変str既にr値なければならないが、それはL値であるように、コンパイラが作用bar関数内です。

誰かがこの動作についての標準への言及を引用できますか? ありがとう!

+5

名前付きオブジェクトは常にlvalueです。そういうわけで、あなたは「動く」必要があります。 –

答えて

10

strは、値がの値であり、の参照のみです。しかしそれはまだ基準であり、左辺値です。変数としてstrを使用することもできます。これは、一時的な右辺値ではなく、左辺値であることを意味します。

左辺は§3.10.1.1によれば、(左辺値が代入式の左側に表示される可能性があるため、いわゆる、歴史的に、)

左辺値が指定関数またはオブジェクトです。【はEがポインタ型の式である場合、* EEにオブジェクトまたは関数を参照する左辺値表現です。別の例として、戻り値の型が左辺値参照である関数を呼び出した結果は左辺値です。 - 端例]

そして右辺§3.10.1.4によれば、次のとおりです

右辺値が右に表示される可能性があるため、右辺値は(そう、歴史的に、呼び出さ代入の手元側 式)は、と関連付けられていない値である一時オブジェクト(12.2)またはそのサブオブジェクト、またはのxvalueです。これに基づいて

strは一時的オブジェクトではなく、それは、strと呼ばれるオブジェクトを有する)オブジェクトに関連付けられたであり、そしてそれは右辺値ではありません。

左辺値の例ではポインタを使用していますが、これは参照と同じであり、当然のところ、値の参照(これは特別なタイプの参照のみです)です。あなたは(だけで右辺値ではなく、左辺値を受け入れる)fooを呼び出すことstd::moveそれをする必要がありますので

だから、あなたの例では、strは、左辺値です。

7

「右辺値参照」の「右辺値が」基準から結合することができる値の種類を指す:

  • 左辺値参照は右辺値参照は右辺値
  • に結合することができる左辺値
  • に結合することができます
  • (+もう少し)

これだけです。重要なのは、ではなく、の値を参照すると、を使用するとという参照が使用されます。参照変数(任意の種類の参照)を取得すると、その変数の名前付けに使用されるid式は常に左辺値になります。右辺値は、一時的な値として、または関数呼び出し式の値として、またはキャスト式の値として、または崩壊の結果として、またはthisとしてのみ発生します。

特定のアナロジーは、ポインタを逆参照してここにありますは:ポインタを参照解除することは関係なく、そのポインタが得られたか、常に左辺値ではありません:*p*(p + 1)*f()はすべて左辺値です。あなたがその事によってどのように来たかは問題ではありません。一度それを持っていれば、それは左利きです。

少し前に戻って、おそらく最も興味深いのは、rvalue参照がrvalueをlvalueに変換するメカニズムであるということです。変更可能な左辺値を生成するC++ 11より前のこのようなメカニズムは存在しませんでした。左端から右端への変換は、当初から言語の一部となっていましたが、左端から右端への変換の必要性を発見するまでにはずっと時間がかかりました。

3

私の質問はなぜですか?

「なぜ」の答えを強調したいので、別の回答を追加します。

の値は右値にバインドすることができますが、使用すると左値として扱われます。たとえば、次のように

struct A {}; 

void h(const A&); 
void h(A&&); 

void g(const A&); 
void g(A&&); 

void f(A&& a) 
{ 
    g(a); // calls g(const A&) 
    h(a); // calls h(const A&) 
} 

右辺値がf()aパラメータにバインドすることができますが、一度バインドされ、aは現在、左辺値として扱われます。特に、オーバーロードされた関数g()h()を呼び出すと、const A&(lvalue)のオーバーロードが解決されます。まずg()の「移動バージョンは」そうaをくすねることになる、と呼ばれることになるし、その後くすねah()の移動過負荷に送信されます:f内右辺値としてaの治療は、エラーが発生しやすいコードにつながるであろう。

Reference

関連する問題