web-dev-qa-db-ja.com

関数から返されたオブジェクトは、使用されていない場合でも作成されますか?

次のコードについて考えてみます。 doStuff()が呼び出されたが、戻り値が使用されていない場合はどうなりますか? SomeClassはまだ作成されていますか?もちろん、作成自体には重要な副作用がありますが、コピーコンストラクターも同様であり、RVO /コピーの省略では省略されます。

SomeClass doStuff(){
    //...do stuff
    return SomeClass( /**/);
}

SomeClass some_object = doStuff();
doStuff(); //What happens here?

(編集:これをGCC -O3でテストしました。オブジェクトは構築され、すぐに破棄されます)

19
AdyAdy

RVOとコピーの省略に関しては誤解があるように感じます。関数の戻り値が作成されないという意味ではありません。それは常に作成されます、それは実装がやることから取り締まることができるものではありません。

副作用にもかかわらず、コピーを削除することになると、唯一の余裕は、中間の人を切ることです。呼び出しの結果を使用してオブジェクトを初期化する場合、標準では、関数を直接初期化するために、ターゲットオブジェクトをプラグインすることが許可されています。

(結果を使用して)ターゲットオブジェクトを提供しない場合は、関数呼び出しを含む完全な式の一部として、一時オブジェクトを実体化して破棄する必要があります。

だからあなたの例で少し遊ぶために:

doStuff(); // An object is created and destroyed as part of temporary materialization
           // Depending on the compilers analysis under the as-if rule, there may be
           // further optimization which gets rid of it all. But there is an object there 
           // formally.

std::Rand() && (doStuff(), std::Rand());
// Depending on the result of std::Rand(), this may or may not create an object.
// If the left sub-expression evaluates to a falsy value, no result object is materialized.
// Otherwise, one is materialized before the second call to std::Rand() and 
// destroyed after it.
22
StoryTeller

コンパイラは、副作用がある場合でも、特定の場合に不要なコピーを削除する場合があります。

コンパイラは、副作用がある場合、オブジェクトの存在全体を削除しない場合があります。

副作用がなければ結果は観察できないので、存在が起こったかどうかは事実上問題ではありません。

tl; dr:標準には非常に具体的なエリジオンの機会がリストされていますが、これはその1つではありません。