2017-07-27 12 views
-3

コミュニティからの幅広い回答があるため、スタックオーバーフローユーザーからの実装固有の回答を批判したいと考えています。returnステートメントのオブジェクト構築時にstd :: move()がRVOを助けるか、防止しますか?

どれがベストプラクティス(最適化が最大)ですか?

// version 1 
MyObject Widget::GetSomething() { 
    return MyObject(); 
} 

// version 2 
MyObject Widget::GetSomething() { 
    return std::move(MyObject()); 
} 

// version 3 
MyObject Widget::GetSomething() { 
    auto obj = MyObject() 
    return obj; 
} 

// version 4 
MyObject Widget::GetSomething() { 
    auto obj = MyObject() 
    return std::move(obj); 
} 

EDIT: 直接、敬意答えを、Yakkに感謝します。 [受け入れ可能な回答]

+3

「コミュニティからの広範な回答」 - 例?これはAFAIKのコンセンサスでは悪いことです。 –

+0

あなたは私が好きなものを逃しました: 'return {}; ' –

+0

「最大の最適化を提供する」は、本質的に実装固有の質問です –

答えて

2
// version 1 
MyObject Widget::GetSomething() { 
    return MyObject(); 
} 

これはMyObjectでコピー可能です。実行時には、標準でelisonが許可されているので、妥当な設定の "実"コンパイラを使用してコピーは作成されません。

C++ 11または14では、オブジェクトは移動可能またはコピー可能である必要があります。 Elisionは残っている。移動もコピーも行われません。

C++では、ここに移動またはコピーがありません。

いずれの場合でも、実際には戻り値にMyObjectが直接構築されます。

// version 2 
MyObject Widget::GetSomething() { 
    return std::move(MyObject()); 
} 

これはC++ 03では無効です。

C++ 11以降では、MyObjectが戻り値に移動されます。実行は実行時に行われなければなりません(as-ifを排除することはできません)。

// version 3 
MyObject Widget::GetSomething() { 
    auto obj = MyObject(); 
    return obj; 
} 

バージョン1と同じですが、C++ 17はC++ 11/14のように動作します。さらに、ここでの溶離はより脆弱である。一見無害な変更は、コンパイラに実際にobjを移動させる可能性があります。

ここでは、C++ 11/14/17(およびC++ 03では2つのコピー)では理論的に2つの移動が省略されています。最初の溶離は安全で、2番目の溶離は安全です。

// version 4 
MyObject Widget::GetSomething() { 
    auto obj = MyObject(); 
    return std::move(obj); 
} 

は、実際には、これは単なるバージョン2(C++ 03でコピー)の余分な動きobjを構築する上で発生するように振る舞うが、それは省略されているので、何も実行時に起こりません。

Elisionを使用すると、コピー/移動の副作用を排除できます。オブジェクトのライフタイムが1つのオブジェクトにマージされ、移動/コピーが削除されます。コンストラクタはまだ存在しなければならず、呼び出されることはありません。

回答

1と3の両方が同じランタイムコードにコンパイルされます。 3はやや脆弱です。

2と4の両方が同じランタイムコードにコンパイルされます。それは決して1/3より速いはずはありませんが、実行していないことを証明したコンパイラによって移動を取り除くことができれば、実行時と同じランタイムコードにコンパイルすることができます。これは保証されておらず、非常に脆弱です。

したがって1>=3>=2>=4は、実際には速度が遅く、それ以外の速度が同じである「より脆い」コードは<=です。あなたはif文を持っていた場合

は1よりも3が遅く作ることができる場合の例としては、:

// version 3 - modified 
MyObject Widget::GetSomething() { 
    auto obj = MyObject(); 
    if (err()) return MyObject("err"); 
    return obj; 
} 

突然、多くのコンパイラではなくobjをelidingの戻り値にobjを移動することを余儀なくされ、一緒に戻り値。

関連する問題