9

ここでは、RVO をいつ実行できるかについて多くの議論が行われていますが、実際にいつ実行されるかについてはあまり議論されていません。何度も述べているように、標準に従って RVO を保証することはできませんが、RVO の最適化が成功するか、対応するコードがコンパイルに失敗することを保証する方法はありますか?

これまでのところ、RVO が失敗したときにコードがリンク エラーを発行するようにすることに部分的に成功しました。このため、コピー コンストラクターを定義せずに宣言します。x(x&&)明らかに、これは、1 つまたは両方のコピー コンストラクター、つまりとを実装する必要があるまれではないケースでは、堅牢でも実行可能でもありませんx(x const&)

これは私の 2 番目の質問につながります。コンパイラの作成者は、ユーザー定義のコピー コンストラクターが存在する場合に RVO を有効にすることを選択し、既定のコピー コンストラクターのみが存在する場合には有効にしないのはなぜですか?

3 番目の質問:プレーン データ構造に対して RVO を有効にする他の方法はありますか?

最後の質問 (約束):私のテスト コードを、gcc と clang で観察した以外の動作にするコンパイラを知っていますか?

問題を示す gcc 4.6、gcc 4.8、clang 3.3 のサンプル コードを次に示します。この動作は、一般的な最適化またはデバッグ設定には依存しません。もちろん、オプション--no-elide-constructorsはそれが言うことを行います。つまり、RVO をオフにします。

#include <iostream>
using namespace std;

struct x
{
    x () { cout << "original x address" << this << endl; }
};
x make_x ()
{
    return x();
}

struct y
{
    y () { cout << "original y address" << this << endl; }
    // Any of the next two constructors will enable RVO even if only
    // declared but not defined. Default constructors will not do!
    y(y const & rhs);
    y(y && rhs);
};
y make_y ()
{
    return y();
}

int main ()
{
    auto x1 = make_x();
    cout << "copy of  x address" << &x1 << endl;
    auto y1 = make_y();
    cout << "copy of  y address" << &y1 << endl;
}

出力:

original x address0x7fff8ef01dff
copy of  x address0x7fff8ef01e2e
original y address0x7fff8ef01e2f
copy of  y address0x7fff8ef01e2f

RVO はプレーンなデータ構造でも動作しないようです:

#include <iostream>

using namespace std;

struct x
{
    int a;
};

x make_x ()
{
    x tmp;
    cout << "original x address" << &tmp << endl;
    return tmp;
}

int main ()
{
    auto x1 = make_x();
    cout << "copy of  x address" << &x1 << endl;
}

出力:

original x address0x7fffe7bb2320
copy of  x address0x7fffe7bb2350

更新:一部の最適化は RVO と非常に簡単に混同されることに注意してください。のようなコンストラクタ ヘルパーmake_xがその例です。最適化が実際に標準によって強制されているこの例を参照してください。

4

3 に答える 3

2
  1. そのような保証をする方法はないと思います。RVO は最適化であるため、コンパイラは特定のケースでそれを使用することが実際には最適化解除であると判断し、そうしないことを選択する場合があります。

  2. 最初のコード スニペットを参照していると思います。ideone.com32 ビット ビットのコンパイルでは、最適化がまったく有効になっていない場合でも、g++ 4.4、4.5、または 4.8 (を介して) でアサーションを再現できません。64 ビットのコンパイルでは、RVO なしの動作を再現できます。これは、g++ の 64 ビット コード生成バグのようなにおいがします。

  3. 実際に私が観察した(2)のがバグである場合、バグが修正されると機能します。

  4. Sun CCは、32 ビット コンパイルでも特定の例を RVOしないことを確認できます。

ただし、アドレスを出力するためのイントロスペクション コードがコンパイラに最適化を禁止させているのではないかと思います (たとえば、エイリアシングの問題を防ぐために最適化を禁止する必要がある場合があります)。

于 2013-10-09T15:17:10.880 に答える
0

コンパイラの作成者は、ユーザー定義のコピー コンストラクターが存在する場合に RVO を有効にすることを選択し、既定のコピー コンストラクターのみが存在する場合には有効にしないのはなぜですか?

標準がそう言っているので:

C++14、12.8/31:

特定の基準が満たされると、コピー/移動操作用に選択されたコンストラクターおよび/またはオブジェクトのデストラクタに副作用がある場合でも、実装はクラス オブジェクトのコピー/移動構築を省略できます。

C++14、12.8/32

コピー操作の省略の基準が満たされているか、ソース オブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値によって指定されているという事実を除いて満たされる場合、コピーのコンストラクターを選択するためのオーバーロードの解決は次のとおりです。オブジェクトが右辺値によって指定されたかのように最初に実行されます。オーバーロードの解決が失敗した場合、または選択されたコンストラクターの最初のパラメーターの型がオブジェクトの型 (おそらく cv 修飾) への右辺値参照でない場合、オブジェクトを左辺値と見なしてオーバーロードの解決が再度実行されます。[ 注: この 2 段階のオーバーロード解決は、コピーの省略が発生するかどうかに関係なく実行する必要があります。省略が実行されない場合に呼び出されるコンストラクターを決定し、呼び出しが省略された場合でも、選択されたコンストラクターにアクセスできる必要があります。—終わりのメモ]

RVO (およびその他のコピー省略) はオプションであることを覚えておく必要があります。

RVO が作動するため、コンパイラでコンパイルされるコピー/移動コンストラクタ/代入が削除されたコードを想像してください。次に、その完全にコンパイルされたコードを別のコンパイラに移動すると、合法的にコンパイルに失敗します。これは受け入れられません。

これは、何らかの理由でコンパイラが RVO 最適化を実行しないことを決定した場合でも、コードは常に有効でなければならないことを意味します。

于 2015-05-19T15:36:37.710 に答える