web-dev-qa-db-ja.com

関数のr値パラメーター

関数間で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-値

誰かがこの振る舞いについての標準への参照を引用できますか?ありがとう!

18
Biagio Festa

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

lvalueは、§3.10.1.1によると:

lvalue(歴史的には、左辺値が代入式の左側に表示される可能性があるため、いわゆる)は、関数またはオブジェクトを指定します。 [[〜#〜] e [〜#〜]がポインタ型の式である場合、* E[〜#〜] e [〜#〜]ポイントであるオブジェクトまたは関数を参照する左辺値式です。別の例として、戻り値の型が左辺値参照である関数を呼び出した結果は左辺値です。 —例の終了]

そしてrvalueは、§3.10.1.4によると:

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

これに基づくと、strは一時オブジェクトではなく、isオブジェクト(strと呼ばれるオブジェクト)に関連付けられているため、右辺値ではありません。 。

左辺値の例ではポインターを使用していますが、参照の場合も、当然、右辺値の参照(特殊なタイプの参照のみ)でも同じです。

したがって、あなたの例では、stris a lvalueなので、std::movefooを呼び出します(左辺値ではなく右辺値のみを受け入れます)。

11
Rakete1111

「右辺値参照」の「右辺値」は、参照が可能な値の種類を指しますバインド

  • 左辺値参照は左辺値にバインドできます
  • 右辺値参照は右辺値にバインドできます
  • (+もう少し)

これですべてです。重要なのは、not参照時に取得する値を参照しますse参照です。参照変数(あらゆる種類の参照!)を取得すると、その変数に名前を付けるid式は常に左辺値になります。右辺値は、一時的な値として、または関数呼び出し式の値として、またはキャスト式の値として、または減衰またはthisの結果としてのみ実際に発生します。

ここには、ポインターの逆参照との類似点があります。ポインターの逆参照は、そのポインターがどのように取得されたかに関係なく、常に左辺値です。_*p_、*(p + 1)*f()はすべて左辺値です。 。どうやって来たのかは関係ありません。あなたがそれを手に入れたら、それは左辺値です。

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

7
Kerrek SB

今の私の質問はなぜですか?

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

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&_(左辺値)のオーバーロードに解決されます。 af内の右辺値として扱うと、エラーが発生しやすいコードになります:最初にg()の「移動バージョン」が呼び出され、aが盗まれる可能性があります。次に、盗まれたah()の移動オーバーロードに送信されます。

参照

3
Howard Hinnant