2

私はこのコードを持っています:

#include <iostream>

using std::cout;
using std::endl;

struct Int {
    const int& val;
};

Int test(){
    return Int {30};
}

int main()
{
    Int i = Int{30};

    cout << i.val << endl;

    Int j = test();

    cout << j.val << endl;

    return 0;
}

でコンパイルすると、次の-std=c++11 -O2ように出力されます。

30
0

そして警告:

main.cpp: In function 'int main()':

main.cpp:18:15: warning: '<anonymous>' is used uninitialized in this function [-Wuninitialized]

     cout << j.val << endl;

i.valダングリングリファレンスですか?私が理解している限り、Int{30}一時的なものはセミコロンで破棄され、すでに破棄されているi.val一時的なものにバインドされます。valそれが正しいか?

そして、なぜコンパイラjは初期化されておらず、j.val0 であると言うのですか?

4

1 に答える 1

1

私が最初に与えた答えは、最初の参照に関して正しくありませんでした: 一時変数は通常、それらが作成された完全な式の最後で破棄されますが、参照が一時変数またはいくつかの特定のケースがない限り、一時的なサブオブジェクトに (12.2 [class.temporary] パラグラフ 5 に従って):

  1. クラスのメンバー初期化子リストから参照メンバーにバインドされた一時的なものは、コンストラクターの最後で破棄されます。オブジェクトが最終的にどこに存在するかがわからないため、コンパイラーは、一時的なものを格納せずに一時的な生命を拡張する機会がありません。
  2. 関数呼び出しでパラメーターに一時的にバインドされたものは、完全な式の終わりまで存続します (パラメーターも同様です。関数内でパラメーターをバインドしても、一時的なライフタイムは延長されません)。
  3. return ステートメント内の参照に一時をバインドする場合、返された参照はその有効期間を延長しません。つまり、古い参照が返されます (参照を返す唯一の用途は、別の場所に保持されていて、関数のオブジェクトにないオブジェクトを返すことです)。スタック)。
  4. 作成されたオブジェクトの有効期間を予測できず、一時オブジェクトをどこかに割り当てる必要があるため、新しいイニシャライザで一時的にバインドしても、一時オブジェクトの有効期間は延長されません。

is no 句は、初期化子リストを使用しない限り、参照メンバーを一時的な値で直接初期化することを禁止しています。さらに、新しいイニシャライザに関する項目の例には、実際には同様の例が含まれています (12.2 [class.temporary] 段落 5、4 番目の箇条書きの例):

struct S { int mi; const std::pair<int,int>& mp; };
S a { 1, {2,3} };
S* p = new S{ 1, {2,3} }; // Creates dangling reference

この例では、3 行目で初期化された参照がぶら下がっていることを明示的に示していますが、2 行目ではそうしていません。これは規範的なテキストではありませんが、2 行目は問題ないことを示しているようであり、上記の規則によってこの行も合法になるようです。

つまり、発言

Int i = Int{30};

初期化i.valされ、一時的な (からint構築された) は範囲外に30なるまで保持されます。i一方、声明は

return Int {30};

return ステートメントの参照にテンポラリをバインドし、3 番目の箇条書きが適用されます。テンポラリの有効期間は、式の終わりを超えて延長されません。この動作は、これらのオブジェクトへの参照が返された場合でも、名前付きオブジェクトの有効期間を延長しないことと一致しています。

于 2013-11-08T22:39:51.800 に答える