一時

2011-01-12 9 views
14

の寿命を延ばすことは何であるこの一時

const Foo& a = function_returning_Foo_by_value(); 

ではなく、この

Foo& a = function_returning_Foo_by_value(); 

を可能背後にある設計根拠?

2行目に何が間違っている可能性がありますか(最初の行では間違っていないでしょうか)。

+0

これは、Herb Sutterがここで議論したのと同じ質問ではありませんか?http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/? – DumbCoder

+0

@DumbCoder:いいえ、Herb SutterはC++標準への使用を設計し、Fredは標準の背後にある論理的根拠について議論します。 –

答えて

4

私はあなたの質問に答えます...反対に。

なぜ彼らはFoo const& foo = fooByValue();で始まることができましたか?

人生をやや楽にしますが、場所全体に未定義の可能性があります。

Foo const& fooByReference() 
{ 
    return fooByValue(); // error: returning a reference to a temporary 
} 

これは明らかに間違っています。実際、コンパイラはこれを忠実に報告します。 Tomalakのコメントによると、それは標準では要求されていませんが、良いコンパイラが報告しなければなりません。 Clang、gcc、MSVCがそうです。私はコモとiccもそうだと思う。

Foo const& fooByIndirectReference() 
{ 
    Foo const& foo = fooByValue(); // OK, you're allowed to bind a temporary 
    return foo;     // Generally accepted 
} 

これは間違っていますが、より微妙です。問題は、テンポラリの存続期間が、機能の最後に範囲外になるfooの生存期間に縛られていることです。 コピーfooが呼び出し元に渡され、このコピーがetherにポイントします。

私はClangのバグを起こしました.Argyrisはこのケースを診断することができました(本当に忠実です:p)。

Foo const& fooForwarder(Foo const&); // out of line implementation which forwards 
            // the argument 

Foo const& fooByVeryIndirectReference() 
{ 
    return fooForwarder(fooByValue()); 
} 

fooByValueによって作成された一時を忠実に(参照の)コピーを提供fooForwarderの引数の寿命、にバインドされ、コピーそれは今エーテルに指していても、呼び出し元に返されます。

ここでの問題はfooForwarderの実装が完全に標準的であるにもかかわらず、呼び出し元に未定義の動作を引き起こすことです。

しかし、これを診断するには、fooForwarderの実装について知っておく必要があります。これはコンパイラの手の届かないところです。

一時的に参照にバインドされている場合は、返された参照が同じアドレスを共有していないことを確認する必要があります(WPAを除いて)何 ? assert?例外を提起する?それはランタイム・ソリューションなので、明らかに満足できるものではありません。

一時的な参照をバインドするという考えは脆弱です。

+0

"これは明らかに間違っています。実際には、あなたが幸運であれば、コンパイラはこれを忠実に報告します。もしあなたがこれを行うツールチェーンを持っていれば、警告が一定のレベルに設定されていればなどです。この場合、 –

+0

@Tomalak:私は修正します、それは少なくともMSVC、gccとClangによって報告されています。私はComeauとICCもおそらくそうだと思います。 –

0

「可能性がありますが間違っている可能性があります」とは、オブジェクトを変更してすぐに変更を失うということです。このようなミスをしないようにルールが定義されています。この関数を再度呼び出すと、変更を加えたオブジェクトが得られると思うかもしれません。もちろん、コピーを変更したので、あなたはそうではありません。

あなたはそれを交換しようとしているとき、あなたはそれに非constメソッドを呼び出して、一時的に作成する典型的なケースは次のとおりです。

std::string val; 
some_func_that_returns_a_string().swap(val); 

これは、時には非常に便利です。

+0

変更を失うのはなぜですか?私の質問のタイトルを見て、仮想は 'const Foo&'の場合と同じように 'a'だけ生きています。 – fredoverflow

3

非constポインタが一時オブジェクトの存続期間を延ばさない理由は、非const参照が最初に一時オブジェクトにバインドできないためです。その理由がたくさんある

、私は暗黙の拡大変換を含む1古典的な例を紹介します:

struct Foo {}; 
bool CreateFoo(Foo*& result) { result = new Foo(); return true; } 

struct SpecialFoo : Foo {}; 
SpecialFoo* p; 
if (CreateFoo(p)) { /* DUDE, WHERE'S MY OBJECT! */ } 

constの参照が一時を結合させるための理論的根拠は、このような完全に合理的なコードを可能にするということです:

bool validate_the_cat(const string&); 

string thing[3]; 
validate_the_cat(thing[1] + thing[2]); 

この場合、生涯の延長は必要ありません。

+0

このコードが示す問題は、rvaluesが通常の参照にバインドすべきではないということです。 rvaluesが代わりにconst参照にバインドする必要がある理由を示していません。私の目的はどこですか? – Puppy

+1

@David: 'const Foo *&'はconst参照ではありません。あなたは 'Foo * const&'を意味しましたか? –

+0

私の悪い!私はもっ​​と注意する必要があります...私は直感を持ってテストしましたが、間違ったテストを行いました。あなたが正しいです。私は自分自身のばかを作ったコメントを削除しました:) +1 –

2

私は以下のような根拠を理解しています。一時的なものは、範囲外になると破棄されることが予想されます。 あなたがそれを変更しないことを約束した場合、私はあなたの寿命を延ばすことができます。