2016-07-11 12 views
10

のは、この2つの関数を考えてみましょう:C++の戻り値の最適化、複数の無名の戻り

// 1. Multiple returns of the same named object 
string f() { 
    string s; 
    if (something()) 
     return s.assign(get_value1()); 
    else 
     return s.assign(get_value2()); 
} 

// 2. Multiple returns, all of unnamed objects 
string g() { 
    if (something()) 
     return get_value1(); 
    else 
     return get_value2(); 
} 

これらの各機能は、実際にRVOの面でどのように動作するかはもちろん、コンパイラに依存します。しかし、両方のRVOが共通であると仮定するのは間違いありませんか?


p.s.(答えを参照)機能#1は、次のことを意図していた。

string f() { 
    string s; 
    if (something()) 
     return s; 
    s.assign(get_value()); 
    return s; 
} 

答えて

12

#1の場合、NRVOが起こることないを保証されている、つまり、あなたはsからのコピーを取得することが保証されています関数の戻り値可能ならばこの場合、あなたはまた

return std::move(s.assign(get_value1())); 

を行う方がいいでしょう、NRVOフレンドリーであることを機能を書き換え:

string f() { 
    string s; 
    if (something()) 
     s.assign(get_value1()); 
    else 
     s.assign(get_value2()); 
    return s; 
} 

コンパイラもNRVOを考慮する前に、いくつかの標準的な要件がする必要があります満たした。ここで満足されないのは、returnステートメントの式が変数の名前でなければならないということです。 s.assign(...)は名前ではなく、より複雑な表現です。あなたはNRVOのためにreturn s;のようなものを考える必要があります。 get_value機能がstring(またはconst string)を返すと仮定#2については

、あなたが最も可能性の高い現代の任意のコンパイラでRVOを持つことになり、そして、すべてはC++ 17の批准とうまくいけば、RVOは保証になりますをC++ 17モードに準拠させたコンパイラ(NRVOの保証はありません)。

cppreference.comの(N)RVO(コピーエリートと呼ばれています)についての非常に良好で包括的な情報があります。あなたからRVOを得るか、


は、私は現在のコンパイラの状態を確認することを決めたので、私は#2用の更新プログラム3

GCC 6.1.0、クラン3.8.0とMSVC 2015年いくつかのテストをしました3つのコンパイラ(すべての文は、解析するのに十分簡単です)。

また、上記の "NRVOフレンドリー"のような構成の3つのコンパイラーすべてからNRVOを取得します(MSVCの場合は、最適化を有効にする必要があります)。

しかし、

string f() { 
    string s; 
    if (something()) 
     return s; 
    s.assign(get_value()); 
    return s; 
} 

GCCやクランのような機能のためにNRVOを行うが、MSVCはしていません。しかし、それはsから戻り値に移動します。これは標準準拠です。別の例として

string f() { 
    string s; 
    if (something()) 
     return get_value1(); 
    if (something_else()) 
     return get_value2(); 
    s.assign(get_value3()); 
    return s; 
} 

すべての3つのコンパイラは、最初の2つのreturn秒間RVOを行い、第三のいずれかのsから移動します。

+0

返信いただきありがとうございます。私は 'get_value'を' string'と評価すると思いました。そして例を圧縮するときに私は過剰になりました。#1の意味は、実際には 'string sのようになります。 if(sthg()){return s;} s.assign(get_value()); return s; '。私が不注意であれば、 "コピーエリシエーション"は起こりません。私の質問は主に2番目のケースに焦点を当てていたので、私はそれを解決済みとしてマークし、さらに編集しません。 –

+0

@ n.caillouそのコンストラクションは、コピーエリジョンの標準要件のすべてのチェックボックスにチェックを入れ、GCCとClangからはNRVOを取得しますが、MSVC(2015 Update 3)からはNRVOを取得しないように見えます。それでもそれを混乱させる。しかし、 's'から戻り値への動きを生成します。これは、標準準拠です。あなたのケースでMSVCが重要な場合は、 'return s;'のためにある場所で返すように関数を書き直すほうが良いでしょう。 3つのコンパイラすべてで、複数の 'return get_value();'がうまくいきます - prvaluesは、コンパイラが分析する方が簡単です。 – bogdan

+0

2番目の考えでは、私はこのコメントを答えに移すと思います。 – bogdan