関数間でr値が渡されるときのc ++の動作について疑問に思っていました。
この単純なコードを見てください:
#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;
}
bar
関数内にいるときは、move
関数を呼び出すためにfoo
関数を使用する必要があることを知っています。今の私の質問はなぜですか?
私がbar
関数の中にいるとき、変数str
はすでにr-valueであるはずですが、コンパイラーはl-値。
誰かがこの振る舞いについての標準への参照を引用できますか?ありがとう!
str
は右辺値参照です。つまり、右辺値のみへの参照です。しかし、それはまだ参照であり、左辺値です。 str
を変数として使用できます。これは、一時的な右辺値ではなく、左辺値であることも意味します。
lvalueは、§3.10.1.1によると:
lvalue(歴史的には、左辺値が代入式の左側に表示される可能性があるため、いわゆる)は、関数またはオブジェクトを指定します。 [例:[〜#〜] e [〜#〜]がポインタ型の式である場合、* E[〜#〜] e [〜#〜]ポイントであるオブジェクトまたは関数を参照する左辺値式です。別の例として、戻り値の型が左辺値参照である関数を呼び出した結果は左辺値です。 —例の終了]
そしてrvalueは、§3.10.1.4によると:
rvalue(歴史的には、右辺値が代入式の右側に表示される可能性があるため、いわゆる)はxvalue、一時オブジェクト(12.2)またはそのサブオブジェクトです。 、またはオブジェクトに関連付けられていない値。
これに基づくと、str
は一時オブジェクトではなく、isオブジェクト(str
と呼ばれるオブジェクト)に関連付けられているため、右辺値ではありません。 。
左辺値の例ではポインターを使用していますが、参照の場合も、当然、右辺値の参照(特殊なタイプの参照のみ)でも同じです。
したがって、あなたの例では、str
is a lvalueなので、std::move
foo
を呼び出します(左辺値ではなく右辺値のみを受け入れます)。
「右辺値参照」の「右辺値」は、参照が可能な値の種類を指しますバインド:
これですべてです。重要なのは、not参照時に取得する値を参照しますse参照です。参照変数(あらゆる種類の参照!)を取得すると、その変数に名前を付けるid式は常に左辺値になります。右辺値は、一時的な値として、または関数呼び出し式の値として、またはキャスト式の値として、または減衰またはthis
の結果としてのみ実際に発生します。
ここには、ポインターの逆参照との類似点があります。ポインターの逆参照は、そのポインターがどのように取得されたかに関係なく、常に左辺値です。_*p
_、*(p + 1)
、*f()
はすべて左辺値です。 。どうやって来たのかは関係ありません。あなたがそれを手に入れたら、それは左辺値です。
少し後退すると、おそらくこれすべての中で最も興味深い側面は、右辺値参照が右辺値を左辺値に変換するメカニズムであるということです。 C++ 11より前には、可変左辺値を生成するようなメカニズムは存在していませんでした。左辺値から右辺値への変換は当初から言語の一部でしたが、右辺値から左辺値への変換の必要性を発見するのにはるかに長い時間がかかりました。
今の私の質問はなぜですか?
「なぜ」への答えを強調したいので、別の答えを追加します。
named右辺値参照は右辺値にバインドできますが、使用すると左辺値として扱われます。例えば:
_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&
_(左辺値)のオーバーロードに解決されます。 a
をf
内の右辺値として扱うと、エラーが発生しやすいコードになります:最初にg()
の「移動バージョン」が呼び出され、a
が盗まれる可能性があります。次に、盗まれたa
がh()
の移動オーバーロードに送信されます。
参照 。