これを許可する設計上の根拠は何ですか
const Foo& a = function_returning_Foo_by_value();
しかし、これではありません
Foo& a = function_returning_Foo_by_value();
?
2 行目で何が問題になる可能性がありますか (最初の行ではまだ問題が発生していません)。
私はあなたの質問に答えます...逆に。
なぜ彼らはFoo const& foo = fooByValue();
から始めることを許可したのですか?
これにより、作業が (多少) 楽になりますが、あらゆる場所で未定義の動作が発生する可能性があります。
Foo const& fooByReference()
{
return fooByValue(); // error: returning a reference to a temporary
}
これは明らかに間違っており、実際、コンパイラは忠実に報告します。トマラクのコメントによると、標準では義務付けられていませんが、優れたコンパイラはそれを報告する必要があります。Clang、gcc、および MSVC が行います。コモーとiccもそうだと思います。
Foo const& fooByIndirectReference()
{
Foo const& foo = fooByValue(); // OK, you're allowed to bind a temporary
return foo; // Generally accepted
}
これは間違っていますが、より微妙です。問題は、temporary の有効期間が の有効期間にバインドされており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
? 例外を発生させますか? また、これはランタイム ソリューションにすぎないため、明らかに満足のいくものではありません。
テンポラリを参照にバインドするという考えは脆いものです。
非 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]);
この場合、有効期間の延長は必要ないことに注意してください。
私はその理論的根拠を次のように理解しています。一時的なものは、スコープ外になると破棄されることが期待されています。変更しないと約束していただければ、寿命を延ばして差し上げます。
「うまくいかない可能性がある」とは、オブジェクトを変更するとすぐに変更が失われることです。したがって、このような間違いをしないようにルールが定義されています。関数をもう一度呼び出すと、変更を加えたオブジェクトが得られると思うかもしれませんが、もちろん、コピーを変更したために得られません。
一時メソッドを作成してから非constメソッドを呼び出す一般的なケースは、それを交換する場合です。
std::string val;
some_func_that_returns_a_string().swap( val );
これは非常に便利な場合があります。