web-dev-qa-db-ja.com

const参照クラスのメンバーは、テンポラリの寿命を延ばしますか?

これはなぜですか:

#include <string>
#include <iostream>
using namespace std;

class Sandbox
{
public:
    Sandbox(const string& n) : member(n) {}
    const string& member;
};

int main()
{
    Sandbox sandbox(string("four"));
    cout << "The answer is: " << sandbox.member << endl;
    return 0;
}

以下を出力します。

答えは:

の代わりに:

答えは:4

154
Kyle

localconst参照のみが寿命を延ばします。

この規格は、8.5.3/5、[dcl.init.ref]、参照宣言の初期化子に関するセクションでそのような動作を指定しています。この例の参照は、コンストラクターの引数nにバインドされており、オブジェクトnがバインドされると無効になります。

ライフタイム拡張は、関数の引数を通じて推移的ではありません。 §12.2/ 5 [class.temporary]:

2番目のコンテキストは、参照が一時にバインドされるときです。参照がバインドされているテンポラリ、または一時オブジェクトがバインドされているサブオブジェクトの完全なオブジェクトであるテンポラリは、以下に指定されている場合を除き、リファレンスの存続期間中保持されます。コンストラクターのctor-initializer(§12.6.2[class.base.init])の参照メンバーへの一時的なバインドは、コンストラクターが終了するまで持続します。関数呼び出し(§5.2.2[expr.call])の参照パラメーターへの一時的なバインドは、呼び出しを含む完全な式が完了するまで持続します。

154
Potatoswatter

これが何が起こったのかを説明する最も簡単な方法です:

Main()で文字列を作成し、コンストラクタに渡しました。この文字列インスタンスは、コンストラクター内にのみ存在していました。コンストラクター内で、このインスタンスを直接指すようにメンバーを割り当てました。スコープがコンストラクターを離れると、文字列インスタンスが破棄され、メンバーは存在しなくなった文字列オブジェクトを指し示しました。 Sandbox.memberがそのスコープ外の参照を指すようにしても、それらの外部インスタンスはスコープ内に保持されません。

希望する動作を表示するようにプログラムを修正する場合は、次の変更を行います。

int main()
{
    string temp = string("four");    
    Sandbox sandbox(temp);
    cout << sandbox.member << endl;
    return 0;
}

これで、tempはコンストラクターの最後ではなく、main()の最後でスコープ外に渡されます。ただし、これは悪い習慣です。メンバー変数は、インスタンスの外部に存在する変数への参照であってはなりません。実際には、その変数がいつスコープの外に出るかはわかりません。

Sandbox.memberをconst string member;として定義することをお勧めします。これにより、メンバー変数を一時パラメーター自体として割り当てる代わりに、一時パラメーターのデータがメンバー変数にコピーされます。

23
Squirrelsama

技術的に言えば、このプログラムは実際に標準出力(最初はバッファストリーム)に何も出力する必要はありません。

  • _cout << "The answer is: "_ビットは、stdoutのbufferに_"The answer is: "_を送信します。

  • 次に、_<< sandbox.member_ビットは、ndefined behaviorを呼び出すoperator << (ostream &, const std::string &)にぶら下がり参照を提供します。

このため、何も起こらないことが保証されています。プログラムは一見問題なく動作するか、stdoutをフラッシュしなくてもクラッシュする可能性があります。つまり、「答えは:」というテキストは画面に表示されません。

4
Tanz87

サンドボックスコンストラクターが返されると、一時的な文字列がスコープ外になり、その文字列が占めていたスタックが他の目的のために回収されたためです。

一般に、参照を長期間保持することはできません。参照は引数またはローカル変数に適していますが、クラスメンバーには適していません。

0
Fyodor Soikin

明白な答え:あなたは消えた何かを指している。以下が機能します

#include <string>
#include <iostream>
using namespace std;

class Sandbox
{

public:
    const string member = " ";
    Sandbox(const string& n) : member(n) {}//a copy is made

};

int main()
{
    Sandbox sandbox(string("four"));
    cout << "The answer is: " << sandbox.member << endl;
    return 0;
}
0
Prabhu M