前書き
通常T const&
、それに直接バインドされた一時オブジェクトの有効期間を延長できますが、参照がコンストラクタの背後に「隠れている」T&&
場合、これは適用されません。
std::reference_wrapper
は (意図的に) コピー可能であるため、一時オブジェクトが作成されたスコープをハンドルがエスケープするような方法で が使用された場合、参照されるオブジェクトへのハンドルは一時オブジェクトよりも長く存続する可能性がありますstd::reference_wrapper
。
これにより、ハンドルと参照される オブジェクト(つまり、一時オブジェクト) の間で有効期間の不一致が発生します。
LET'S PLAY " MAKE BELIEVE "
以下の違法なスニペットがあると想像してください。ここでstd::reference_wrapper
、一時を受け入れるコンストラクターがあるふりをします。
また、コンストラクタに渡されたテンポラリの有効期間が延長されるふりをしましょう (実際にはそうではありませんが、実際には の直後に「死んで」います(1)
)。
typedef std::reference_wrapper<std::string const> string_ref;
string_ref get_ref () {
string_ref temp_ref { std::string { "temporary" } }; // (1)
return temp_ref;
}
int main () {
string_ref val = get_ref ();
val.get (); // the temporary has been deconstructed, this is dangling reference!
}
一時は自動ストレージ期間で作成されるため、内部のスコープにバインドされたストレージに割り当てられますget_ref
。
get_ref
後で戻ると、一時的なものは破棄されます。これは、元のオブジェクトが存在しないため、 in が無効なオブジェクトを参照することを意味しval
ますmain
。
std::reference_wrapper
上記が、のコンストラクターに一時値を受け入れるオーバーロードがない理由です。
もう一つの例
struct A {
A (std::string const& r)
: ref (r)
{ }
std::string const& ref;
};
A foo { std::string { "temporary " } };
foo.ref = ...; // DANGLING REFERENCE!
std::string { "temporary" }
標準で読み取れるように、の寿命は延長されません。
12.2p5
一時オブジェクト [class.temporary]
参照がバインドされている一時オブジェクト、または参照がバインドされているサブオブジェクトの完全なオブジェクトである一時オブジェクトは、次の例外を除き、参照の存続期間中持続します。
コンストラクターの ctor-initializer (12.6.2) の参照メンバーへの一時的なバインドは、コンストラクターが終了するまで持続します。
関数呼び出し (5.2.2) の参照パラメーターへの一時的なバインドは、呼び出しを含む完全な式が完了するまで持続します。
関数 return ステートメント (6.6.3) の戻り値に一時的にバインドされているものの有効期間は延長されません。一時的なものは、return ステートメントの完全な式の最後で破棄されます。
- new-initializer (5.3.4)の参照への一時的なバインドは、 new-initializer を含む完全な式が完了するまで持続します。
注: コンストラクターは、オブジェクトの構築時に呼び出される「凝った」関数に過ぎないことに注意することが重要です。