14

左辺値はメモリの決定的な領域にバインドされた値ですが、右辺値は一時的な存在であり、必ずしもメモリの決定的な領域を参照するとは限らない式の値です。右辺値が予期される位置で左辺値が使用される場合は常に、コンパイラは左辺値から右辺値への変換を実行してから、評価を続行します。

http://www.eetimes.com/discussion/programming-pointers/4023341/Lvalues-and-Rvalues

一時的な (匿名の) クラス オブジェクトを構築するか、関数から一時的なクラス オブジェクトを返すときはいつでも、オブジェクトは一時的ですが、アドレス指定可能です。ただし、オブジェクトは依然として有効な右辺値です。これは、オブジェクトが a) アドレス指定可能な右辺値であるか、または b) コンパイラが左辺値の使用を想定しているときに、左辺値から右辺値に暗黙的に変換されていることを意味します。

例えば:

class A
{
public:
    int x;
    A(int a) { x = a; std::cout << "int conversion ctor\n"; }
    A(A&) { std::cout << "lvalue copy ctor\n"; }
    A(A&&) { std::cout << "rvalue copy ctor\n"; }
};
A ret_a(A a) 
{
    return a;
}

int main(void)
{
    &A(5); // A(5) is an addressable object
    A&& rvalue = A(5); // A(5) is also an rvalue
}

また、関数によって返される一時オブジェクト(次の場合) は、次のコード セグメントのように左辺値であることもわかっています。a

int main(void)
{
    ret_a(A(5));
}

次の出力が得られます。

int conversion ctor

lvalue copy ctor

ret_a実引数を使用した関数の呼び出しが、値 5 で関数の仮引数を構築A(5)する変換コンストラクターを呼び出すことを示します。A::A(int)a

関数の実行が完了すると、 を引数として使用して一時Aオブジェクトを作成し、 を呼び出します。ただし、オーバーロードされたコンストラクターのリストから削除した場合、返された一時オブジェクトは引き続き右辺値参照コンストラクターと一致します。aA::A(A&)A::A(A&)A::A(A&&)

これは私がよく理解していないことです:オブジェクトaが右辺値参照と左辺値参照の両方にどのように一致するのでしょうか? A::A(A&)の方がより適切に一致することは明らかですA::A(A&&)(したがってa、左辺値である必要があります)。ただし、仮引数aが左辺値である場合、右辺値参照は左辺値に初期化できないため、 への呼び出しと一致することはできませんA::A(A&&)。コンパイラが左辺値から右辺値への変換を行っている場合、それは簡単です。'A' から 'A&' への変換も些細なことであり、両方の関数が同一の暗黙的な変換シーケンス ランクを持つ必要があるため、オーバーロードされた関数にA::A(A&)との両方がある場合、コンパイラは最適な関数を推測できないはずです。A::A(A&&)候補セット。

さらに、質問(以前に尋ねたもの)は次のとおりです。

特定のオブジェクトが右辺値参照と左辺値参照の両方に一致するにはどうすればよいですか?

4

1 に答える 1

6

私のため:

int main(void)
{
    ret_a(A(5));
}

収量:

int conversion ctor
rvalue copy ctor

(つまり、左辺値ではなく右辺値)。これはコンパイラのバグです。ただし、この動作のルールがわずか数か月前 (2010 年 11 月) に変更されたため、理解できます。これについては、以下で詳しく説明します。

関数の実行が完了すると、 を引数として使用して一時A オブジェクトを作成し、 を呼び出します。aA::A(A&)

実は違う。関数の実行が完了すると、 を引数として使用してret_a一時Aオブジェクトを作成し、 を呼び出します。これは [class.copy]/p33] 1によるものです:aA:A(A&&)

コピー操作の省略の基準が満たされているか、ソース オブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値によって指定されているという事実を除いて満たされる場合、コピーのコンストラクターを選択するためのオーバーロードの解決は次のとおりです。オブジェクトが右辺値によって指定されたかのように最初に実行されます。オーバーロードの解決が失敗した場合、または選択されたコンストラクターの最初のパラメーターの型がオブジェクトの型 (おそらく cv 修飾) への右辺値参照でない場合、オブジェクトを左辺値と見なしてオーバーロードの解決が再度実行されます。[ 注: この 2 段階のオーバーロード解決は、コピーの省略が発生するかどうかに関係なく実行する必要があります。省略されていない場合に呼び出されるコンストラクターを決定し、また、呼び出しが省略された場合でも、選択されたコンストラクターにアクセスできる必要があります。— エンドノート]

A::A(A&&)ただし、コンストラクターを削除するA::A(&)と、戻り値として選択されます。ただし、この場合、a右辺値を使用して構築できないため、引数の構築は失敗します。ただし、今のところそれを無視して、あなたの最終的な質問は次のとおりだと思います。

特定のオブジェクトが右辺値参照と左辺値参照の両方に一致するにはどうすればよいですか?

声明を参照して:

return a;

そして、その答えは、上記の標準草案から引用された段落にありaます。aそれが失敗した場合は、左辺値として使用してオーバーロードの解決が再試行されます。この 2 段階のプロセスは、コピーの省略が許可されているコンテキスト (return ステートメントなど) でのみ試行されます。

C++0x ドラフトは最近変更され、値で渡された引数を返すときに 2 段階のオーバーロード解決プロセスを許可するようになりました (例のように)。これが、私たちが目にしているさまざまなコンパイラからのさまざまな動作の理由です。

于 2011-03-11T00:36:32.660 に答える