1

次のコードがあるとします。

struct obj {
    int i;
    obj() : i(1) {}
    obj(obj &&other) : i(other.i) {}
};

void f() {
    obj o2(obj(obj(obj{})));
}

リリース ビルドでは、実際には 1 つのオブジェクトのみが作成され、コードが実行された場合と結果が同じであるため、move コンストラクターが呼び出されることはないと予想されます。ほとんどのコードはそれほど単純ではありませんが、オプティマイザーが「あたかも」を証明するのを妨げる可能性のある、予測が難しい副作用がいくつか考えられます。

  1. 移動コンストラクタまたはデストラクタのいずれかで、グローバルまたは「外部」のものへの変更。
  2. 移動コンストラクタまたはデストラクタでの潜在的な例外 (いずれにせよおそらく設計が悪い)
  3. 内部カウントまたはキャッシュ メカニズムの変更。

私はこれらのどれも頻繁に使用しないので、後で最適化されるためにインライン化される関数の出入りのほとんどが期待できますか、それとも何かを忘れていますか?

PS最適化が可能だからといって、特定のコンパイラによって最適化が行われるわけではないことを私は知っています。

4

2 に答える 2

7

これは、as-ifルールとは実際には何の関係もありません。コンパイラは、何らかの副作用がある場合でも、移動とコピーを削除することができます。プログラムの結果を変更する可能性があるのは、コンパイラーが実行できる単一の最適化です。§12.8/31から:

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

したがって、コンパイラーは、moveコンストラクター内で何が起こっているかをわざわざ検査する必要はありません。とにかく、ここですべてのmovesを取り除く可能性があります。これを実証するために、次の例を検討してください。

#include <iostream>

struct bad_mover
{
  static int move_count;
  bad_mover() = default;
  bad_mover(bad_mover&& other) { move_count++; }
};

int bad_mover::move_count = 0;

int main(int argc, const char* argv[])
{
  bad_mover b{bad_mover(bad_mover(bad_mover()))};
  std::cout << "Move count: " << bad_mover::move_count << std::endl;
  return 0;
}
  1. コンパイル済みg++ -std=c++0x

    Move count: 0
    
  2. コンパイル済みg++ -std=c++0x -fno-elide-constructors

    Move count: 3
    

ただし、追加の副作用がある移動コンストラクターを提供する理由については疑問があります。副作用に関係なくこの最適化を許可するという考え方は、コピーまたは移動コンストラクターがコピーまたは移動以外のことを実行してはならないということです。コピーまたは移動のあるプログラムは、ない場合とまったく同じである必要があります。

それにもかかわらず、への呼び出しstd::moveは不要です。std::move左辺値式を右辺値式に変更するために使用されますが、一時オブジェクトを作成する式はすでに右辺値式です。

于 2013-02-23T13:06:55.070 に答える
2

使用std::move( tmp(...) )は完全に無意味です。一時的なものはすでに右辺値であるため、右辺値にキャストするためtmpに使用する必要はありません。std::move

この一連の記事をお読みください:速度が必要ですか? 値渡し

Stackoverflow で質問することで、より多くのことを学び、理解を深めることができます。

于 2013-02-23T13:03:24.333 に答える