2016-02-10 4 views
7

このコードサンプルでは、​​Sに渡される一時的な文字列の有効期間に関するルールは何ですか?標準によれば集計参照メンバーと一時的な生涯

struct S 
{ 
    // [1] S(const std::string& str) : str_{str} {} 
    // [2] S(S&& other) : str_{std::move(other).str} {} 

    const std::string& str_; 
}; 

S a{"foo"}; // direct-initialization 

auto b = S{"bar"}; // copy-initialization with rvalue 

std::string foobar{"foobar"}; 
auto c = S{foobar}; // copy-initialization with lvalue 

const std::string& baz = "baz"; 
auto d = S{baz}; // copy-initialization with lvalue-ref to temporary 

:コンストラクタのCTOR-イニシャライザ(12.6.2)で基準部材への一時的なバウンド(N4296で除去)

N4140 12.2 P5.1

コンストラクターが終了するまで続きます。

N4296 12.6.2 P8

MEM-初期における基準部材に結合された一時的発現は悪い形成されています。

したがって、[1]のようなユーザー定義のコンストラクタを持つことは、われわれが望むものではありません。それは最新のC++ 14で不正な形になっていることさえありますか?gccもclangも警告していません。
直接集計の初期化で変更されますか?私はそのように見える、一時的な生涯が延長されます。

コピーの初期化に関して、Default move constructor and reference membersは暗黙的に[2]が生成されると述べています。移動が省略される可能性があることを考慮して、暗黙的に生成された移動コンストラクターにも同じ規則が適用されますか?

a, b, c, dには有効な参照がありますか?

+0

集約初期化の一時ファイルの存続期間の延長から例外がありません。したがって、一時ファイルの存続期間が延長されます。これにより、「直接初期化」の場合に作成された一時的なものの適切な有効期間が保証されます。 – dyp

+0

「その動きは逃げられるかもしれない」ということは何を意味しますか?参照バインディングは省略できません。 'str_'は' other.str'に直接バインドされます。 ( 'std :: move'は効果がありません) –

+0

@ M.Mほとんどのコンパイラは、移動コンストラクタを使用するのではなく直接初期化を実行します。はい。 'std :: move(other).str'は参照のために' other.str'と同じであり、ここでは何の効果もありません – 3XX0

答えて

2

特定の例外がない限り、参照にバインドされた一時オブジェクトの有効期間が延長されます。つまり、そのような例外がない場合、寿命が延長されます。

ごく最近のドラフトから、N4567:

[寿命が延長された第二のコンテキストが 参照が一時的に結合しているときです。一時オブジェクトバウンド

  • (5.1):一時的に 参照基準を除い の寿命参照持続を結合している サブオブジェクトの完全なオブジェクトであること結合した、または一時的なされています関数呼び出し(5.2.2)の参照パラメータへの呼び出しは、呼び出しを含む完全式 の完了まで続きます。
  • (5.2)関数のreturn文(6.6.3)で返される値にバインドされた一時的な存続期間は延長されません。一時的には がreturnステートメントの完全式の終わりに破棄されます。
  • (5.3)new-initializer(5.3.4)のリファレンスへの一時バインドは、new-initializerを含む完全な式 の完了まで続きます。

C++ 11にのみ有意な変化であり、OPは、述べたように、その中にC++ 11(N3337)から参照型のデータメンバーのための追加の例外があった:

  • コンストラクタのctor-initializer(12.6.2)内の参照メンバへの一時的なバインドは、コンストラクタが終了するまで続きます。

これは、(後のC++ 14)CWG 1696で除去し、そしてMEM-イニシャライザを介してデータメンバを参照するために、一時的なオブジェクトを結合することは今悪い形成されています。 OPの例について


:これは一時的std::stringを作成し、それをstr_データメンバーを初期化

struct S 
{ 
    const std::string& str_; 
}; 

S a{"foo"}; // direct-initialization 

S a{"foo"}は集約初期化を使用しているため、mem-initializerは関係ありません。寿命延長の例外は適用されないため、一時的な寿命は参照データメンバstr_の有効期間まで延長されます。前C++ 17で必須コピーの省略に


auto b = S{"bar"}; // copy-initialization with rvalue 

正式には、我々はstr_基準部材に、一時的なstd::stringを結合することによって、一時的なSを初期化し、一時的なstd::stringを作成。その後、一時的にSbに移動します。これにより、リファレンスが「コピー」され、std::stringの有効期間が延長されません。 しかし、実装では、一時的なSからbへの移動は削除されます。これは、一時的なstd::stringの生涯に影響してはなりません。あなたは、以下のプログラムでこれを観察することができます:loudのデストラクタがそのトレース後までx生活のに対し、「メインの終わり」の前に呼び出されること

#include <iostream> 

#define PRINT_FUNC() { std::cout << __PRETTY_FUNCTION__ << "\n"; } 

struct loud 
{ 
    loud() PRINT_FUNC() 
    loud(loud const&) PRINT_FUNC() 
    loud(loud&&) PRINT_FUNC() 
    ~loud() PRINT_FUNC() 
}; 

struct aggr 
{ 
    loud const& l; 
    ~aggr() PRINT_FUNC() 
}; 

int main() { 
    auto x = aggr{loud{}}; 
    std::cout << "end of main\n"; 
    (void)x; 
} 

Live demo

注意。正式には、一時的なloudはそれを作成した完全な表現の終わりに破棄されます。

aggrの移動コンストラクタがユーザー定義の場合、動作は変更されません。 C++ 17で必須コピーエリジオンと

我々はLHS b上のオブジェクトとRHS S{"bar"}上のオブジェクトを識別します。これにより、一時的なライフタイムは、寿命bまで延長されます。 CWG 1697を参照してください。


残りの2つの例では、移動コンストラクタは、単に参照をコピーします。もちろん、移動コンストラクタ(S)は省略することもできますが、参照をコピーするだけであるため、これは観測できません。

+0

おそらく 'auto b = S {" bar "};'はC++ 17で変更されますか? prvaluesへの変更の意図は、 'S s {x};は' auto s = S {x};と完全に一致するはずですが、このケースをカバーする正確な文言はチェックしていません。 –

+0

@MM新しい表現を理解することは簡単ではありませんが(* "初期オブジェクトの表現は目的地オブジェクトを初期化するために使われます" *、17.6.1)、https://wg21.link/cwg1697では 'b'と'S {" bar "}"は同じオブジェクトを参照するので、一時的なライフタイムは 'b'の存続期間まで延長されるべきです。私はコンパイラが現在これを実装しているとは思わない(答えのライブデモを参照)。 – dyp

関連する問題