代入演算子の呼び出し
two = three
一時オブジェクトを右辺値として返します。これは型MyObject
であり、代入演算子の次の呼び出しに渡されます
one = t
(t
一時オブジェクトを参照するために使用します。)
残念ながら、代入演算子はMyObject&
type の右辺値ではなく参照を想定しているため、これはコンパイルされませんMyObject
。
Class
(コードは、大文字やタイプミスなど、さまざまな理由でコンパイルされません。)
ただし、右辺値を取る (つまり、引数を値、const 参照、またはMyObject&&
C++11 を使用している場合は右辺値参照によって受け取る) 代入演算子を定義する場合、呼び出しは機能し、一時オブジェクトは次のようになります。関数にコピーされます。内部的には、割り当てが行われ、別の一時オブジェクトが返されます。
最終的な一時オブジェクトは範囲外になります。つまり、存在しなくなります。その内容にアクセスする方法はありません。
Joachim Pileborg と Benjamin Lindley の有益なコメントに感謝します。
詳細についての要求に答えるには:MyObject
はクラス型であり、C++ 標準には、クラス型の一時オブジェクトのライフサイクルに関するセクション全体が含まれています (セクション 12.2)。さまざまな複雑な状況が詳細に記載されているため、すべてを説明することはしません。ただし、基本的な概念は次のとおりです。
C++ には式の概念があります。式は、宣言やステートメントと同様に、プログラムのコードを構成する基本単位です。たとえば、関数呼び出しf(a,b,c)
は式、または のような代入ですa = b
。式には他の式を含めることができます:a = f(b,c)
代入式にネストされた関数呼び出し。C++ では、完全式の概念も導入されています。前の例でc
は、 は式 の一部であるf(b,c)
だけでなく、 の一部でもあり、それが別の式にネストされていない場合、それは語彙的に を含む完全な式であるとa = f(b,c)
言います。a = f(b,c)
c
標準では、一時オブジェクトが作成されるさまざまな状況が定義されています。そのような状況の 1 つは、関数呼び出しから値によってオブジェクトを返すことです (別名、 prvalue を返す、§6.6.3)。
標準では、そのような一時オブジェクトの有効期間は、それを含む完全な式が完全に評価されたときに終了すると述べています。
[...] 一時オブジェクトは、それらが作成されたポイントを (レキシカルに) 含む完全式 (1.9) を評価する最後のステップとして破棄されます。[...]
(注。その後、標準では、この規則に対するいくつかの例外が定義されています。ただし、代入演算子の戻り値の場合は、そのような例外ではありません。)
では、(クラス型の) オブジェクトが破棄されるとはどういう意味でしょうか? 何よりもまず、そのデストラクタが呼び出されることを意味します (§12.2/3)。また、そのオブジェクトのストレージに安全にアクセスできなくなったことも意味します。そのため、完全な式の評価が終了する前に何らかの方法で一時オブジェクトのアドレスをポインターに格納できた場合、評価が終了した後にそのポインターを逆参照すると、通常、未定義の動作が発生します。
実際には、これは多くの場合、次のことを意味します。一時オブジェクトのライフサイクル全体を 1 つの可能なシナリオで説明します。
- 一時的なストレージを提供するために、完全な式を含む関数が入力されたときに、コンパイラは十分なスタック領域が割り当てられることを確認します (これは、完全な式が実際に評価される前に発生します)。
- 代入式の評価中に、一時が作成されます。コンパイラーは、そのコンストラクターが呼び出されて、割り当てられたスペースを初期化するようにします。
- 次に、テンポラリの内容は、それが含まれる完全な式の評価の過程でアクセスまたは変更される可能性があります。
- 式が完全に評価されると (この場合、この瞬間は代入式を含む行の終わりに対応します)、一時的なデストラクタが呼び出されます。その後、割り当てられたメモリにアクセスすることはもはや安全ではありませんが、実際には、このすべてが発生する関数の評価が終了するまで、そのスペースは現在のスタック フレームの一部であり続けます。
しかし、繰り返しますが、これは起こり得ることのほんの一例です。多くの場合、一時オブジェクトの作成は実際には必要ありません。コンパイラーは、一時ファイルが実際には作成されないことを意味する最適化を実行する場合があります。この場合でも、コンパイラはそれが作成されたことを確認する必要があります。たとえば、必要なコンストラクタとデストラクタが存在することを確認する必要があります (ただし、それらは呼び出されない可能性があります)。