注:この回答はc ++ 11以降にのみ適用されます。「C / C ++」のようなものはなく、異なる言語です。
いいえ、ローカルオブジェクトを値で返す危険はありません。そうすることをお勧めします。しかし、ここでのすべての答えに欠けている重要な点があると思います。他の多くの人は、構造体がコピーされているか、RVOを使用して直接配置されていると述べています。ただし、これは完全には正しくありません。ローカルオブジェクトを返すときに発生する可能性のあることを正確に説明しようと思います。
移動セマンティクス
c ++ 11以降、安全に盗むことができる一時オブジェクトへの参照である右辺値参照がありました。例として、std :: vectorには、ムーブコンストラクターとムーブ代入演算子があります。これらは両方とも一定の複雑さを持ち、移動元のベクトルのデータへのポインターをコピーするだけです。ここでは、移動セマンティクスについて詳しくは説明しません。
関数内でローカルに作成されたオブジェクトは一時的なものであり、関数が戻るとスコープ外になるため、返されたオブジェクトがc ++ 11以降でコピーされることはありません。移動コンストラクターは、返されるオブジェクトで呼び出されます(または、後で説明します)。つまり、大きなベクターのように、高価なコピーコンストラクターで安価な移動コンストラクターを使用してオブジェクトを返す場合、データの所有権のみがローカルオブジェクトから返されたオブジェクトに転送されます。これは安価です。
特定の例では、オブジェクトのコピーと移動に違いはないことに注意してください。構造体のデフォルトのmoveおよびcopyコンストラクターは、同じ操作になります。2つの整数をコピーします。ただし、構造体全体が64ビットCPUレジスタに収まるため、これは少なくとも他のソリューションよりも高速です(間違っている場合は訂正してください。CPUレジスタについてはあまり知りません)。
RVOとNRVO
RVOは戻り値の最適化を意味し、コンパイラが実行する数少ない最適化の1つであり、副作用が発生する可能性があります。c ++ 17以降、RVOが必要です。名前のないオブジェクトを返す場合、呼び出し元が戻り値を割り当てる場所に直接構築されます。コピーコンストラクターも移動コンストラクターも呼び出されません。RVOがないと、名前のないオブジェクトは最初にローカルで構築され、次に返されたアドレスで構築されて移動し、次にローカルの名前のないオブジェクトが破棄されます。
RVOが必要な場合(c ++ 17)または可能性が高い場合(c ++ 17より前)の例:
auto function(int a, int b) -> MyStruct {
return MyStruct{a, b};
}
NRVOは、名前付き戻り値の最適化を意味し、呼び出された関数にローカルな名前付きオブジェクトに対して実行されることを除いて、RVOと同じです。これはまだ標準(c ++ 20)によって保証されていませんが、多くのコンパイラはまだそれを行っています。名前付きのローカルオブジェクトを使用しても、返されるときに最悪の場合は移動されることに注意してください。
結論
値で返さないことを検討する必要がある唯一のケースは、名前が付けられた非常に大きい(スタックサイズのように)オブジェクトがある場合です。これは、NRVOがまだ保証されておらず(c ++ 20以降)、オブジェクトの移動でさえ遅いためです。私の推奨事項、およびCppコアガイドラインの推奨事項は、常に値でオブジェクトを返すことを優先することです(複数の戻り値がある場合は、struct(またはタプル)を使用します)。唯一の例外は、オブジェクトの移動にコストがかかる場合です。その場合は、非const参照パラメーターを使用してください。
C ++の関数から手動で解放する必要があるリソースを返すことは決して良い考えではありません。絶対にしないでください。少なくともstd :: unique_ptrを使用するか、リソースを解放するデストラクタ(RAII)を使用して独自の非ローカルまたはローカル構造体を作成し、そのインスタンスを返します。リソースに独自の移動セマンティクスがない場合は、移動コンストラクターと移動代入演算子を定義する(およびコピーコンストラクター/代入を削除する)こともお勧めします。