2011-01-25

C++03とC++0xにおける一時オブジェクトの寿命の違い

今書いているC++0xの参考書では、一時オブジェクトの寿命に関しても、当然、規格の範囲内で、詳細に解説する。

一時オブジェクトの寿命は、一時オブジェクトが構築された場所からみてのfull-expressionを評価した後である。ただし、これには例外がふたつある。興味深いことに、C++03とC++0xで、例外が違っているのだ。

一時オブジェクトをリファレンスで束縛すると、一時オブジェクトの寿命が、リファレンスの寿命に延長される(ただし例外あり)というのは同じだ。違うのは、もうひとつの方だ。

C++03では、オブジェクトを定義する宣言子の初期化子によって構築された一時オブジェクトの寿命は、そのオブジェクトの初期化終了までとされている。

struct X { } ;

int main()
{
    X x001 = X(), x002 = X(), x003 = X(). ;
}

例えば、上のようなコードがあり、実装がX()という式で、一時オブジェクトを構築するとする。x001のために構築した一時オブジェクトの寿命は、x001の初期化終了までである。

しかし、良く考えてみると、このコードにおけるX()のfull-expressionというのは、initializer内の式である。ということは、これはすでに、一般的なルールの範囲ではないのか。とすると、このような例外的なルールは、必要ないということになる。

C++0xでは、この例外的な定義は消えた。しかし、代わりに、新しいルールが追加された。

C++0xでは、配列の要素を初期化する際に、デフォルトコンストラクターがデフォルト実引数を持っていた場合、ある要素のデフォルトコンストラクター実行における一時オブジェクトの破棄は、次の要素の初期化の「以前にシーケンス(sequenced before)」される。

struct Fat { char [1000] ; } ;

struct X
{
    X( Fat f = Fat() ) { } // デフォルトコンストラクター
} ;

int main()
{
    X a[1000] ;
}

この例では、a[0]からa[999]までの1000個のX型の配列の要素に対し、デフォルトコンストラクターが呼び出される。もし、a[0]がa[1]の前に初期化された場合、a[0]のデフォルトコンストラクター呼び出しによって構築されたFat型の一時オブジェクトは、a[1]を初期化するときには、すでに破棄される。もちろん、あらゆるサイドエフェクトなども、以前にシーケンス(sequenced before)される。

No comments: