一時オブジェクトの寿命




1.リファレンス一時オブジェクトの寿命

・リファレンスに対する初期設定子の一部として使われた場合、そのリファレンスと同じだけ生存する。(ARM12.2)

別の言い方をするとconst参照は一時オブジェクトを束縛する。

list-1 cppll3724

#include <iostream>
#include <string>

using namespace std;

string createString()
{
  return "Safe";
}

int main()
{
  const string& x = createString();
  cout << "x = " << x << "\n";

  return 0;
}


だから、このリファレンスの生存期間が問題になる場合もある。

list-2 cppll2448
class A{
  int  n_;
public:
  A( int x = 5 ) : n_(x) {}
};

class REF{
  A&  a_;
public:
  REF( A& x = A() ): a_(x) {}  // ここの一時オブジェクトA()
};

void main(){
  REF  r1, r2(A(10));
}


Aのコンストラクタを抜けた時点でxがスコープからはずれるので一時オブジェクトA()は破棄されている。よって上記のコードは安全ではない。

ちなみに

list-3
    const string& temp = str1 + str2;




list-4
    const string temp = str1 + str2;


と書いてもほとんど同じコードが生成される。これはオブジェクトを返す関数の最適化の一種で、名前付返却値の最適化と呼ばれる。よって参照で一時オブジェクトを格納する必要性は、あまりない。

参考:
http://www.fides.dti.ne.jp/~oka-t/cpplab-retval-ctor.html

list-5
    int& i = int(1);        //  error
    const int& i = int(1);  //  ok


前者は、非constリファレンスなので一時オブジェクト1を束縛できない。

同じ理由で

list-6
    void    f(int& x);  //  constをつけていない
    に対してf(1);

という呼び出しはエラーになる。

list-7
    void    f(const int& x);


ならばok。

2.リファレンスに束縛されていない一時オブジェクトの寿命

リファレンスに束縛されていない一時オブジェクトの寿命は、それが作成された完全式の終わりまで継続する。(ARM12.2)

完全式というのがわかりにくいが、セミコロン(;)までと思えば良い。

よって、

list-8
    string temp = str1 + str2;


は、セーフ。一時オブジェクトを関数に渡すのも関数から抜けるまで一時オブジェクトが生存していることが保証される

list-9
    f( string("safe") );
    f( string("safe").c_str() );
    f( (string("A") + "B").c_str() );
    class C;に対して f(&C());
    //  C()は一時オブジェクトCを作り、そのコンストラクタ呼び出す構文。


いずれも安全である。(C++仕様上は)

・参考

以下のコードをvc6およびg++でコンパイル&ビルドして動かした。

list-10
#include <iostream>
#include <string>
#include <vector>

class C{
public:
    static C CC(){return C();}
    C(){std::cout << "C()" << std::endl;}
    ~C(){std::cout << "~C()" << std::endl;}
    C CCC(){return C();}
    int a() const {std::cout << "a()" << std::endl;return 1;}
};
int f(int i){
    std::cout << i << std::endl;
    return i;
}
void main(){
    f(f(f(C::CC().CCC().CCC().a())));
}


枠-1 結果
 C()
 C()
 C()
 a()
 1
 1
 1
 ~C()
 ~C()
 ~C()


と、どちらの環境でも同じ結果が出た。どうやら仕様通りにきちんと実装されているようだ。