12

少し驚いたことに (私には)、次の 2 つのプログラムは異なる出力にコンパイルされますが、後者の方がパフォーマンスがはるかに優れています (gcc と clang でテスト済み)。

#include <vector>
int main()
{
    std::vector<int> a(2<<20);
    for(std::size_t i = 0; i != 1000; ++i) {
        std::vector<int> b(2<<20);
        a = b;
    }
}

対。

#include <vector>
int main()
{
    std::vector<int> a(2<<20);
    for(std::size_t i = 0; i != 1000; ++i) {
        std::vector<int> b(2<<20);
        a = std::move(b);
    }
}

bコンパイラが最後の割り当てで xvalue を自動的に考慮し、明示的なstd::moveキャストなしで移動セマンティクスを適用する(またはできない) 理由を誰かが説明してくれませんか?

編集:でコンパイル(g++|clang++) -std=c++11 -O3 -o test test.cpp

4

2 に答える 2

7

コンパイラは as-if ルールを破ることができない

§1.9/1 が述べているように:

この国際標準のセマンティック記述は、パラメータ化された非決定論的抽象マシンを定義します。この国際規格は、適合する実装の構造に要件を課していません。特に、抽象マシンの構造をコピーまたはエミュレートする必要はありません。むしろ、以下で説明するように、抽象マシンの観察可能な動作をエミュレートする(のみ)ために、適合する実装が必要です。

つまり、コンパイラはプログラムの観察可能な動作を変更できません。自動的に (影響がなくても) 割り当てを移動割り当てに変換すると、このステートメントが壊れます。

コピーの省略により、この動作がわずかに変更される可能性がありますが、これは §12.8/31 で規制されています。

移動バージョンを使用する場合は、後者の例のように明示的に要求する必要があります。

于 2014-09-24T09:42:59.887 に答える
5

次のサンプルを見てみましょう (voidからの戻り値の型は無視してくださいoperator=):

#include <iostream>

struct helper
{
    void operator=(helper&&){std::cout<<"move"<<std::endl;}
    void operator=(const helper&){std::cout<<"copy"<<std::endl;}
};

void fun()
{
    helper a;
    {
        helper b;
        a = b;
    }
}

void gun()
{
    helper a;
    {
        helper b;
        a = std::move(b);
    }
}
int main()
{
    fun();
    gun();
}

operator=動作は、引数によって異なります。コンパイラは、観察可能な動作を同じに維持できる場合にのみ、コードを最適化できます。

bから考えるとfun、呼び出しの時点でxvalueは ではありませんxvalueが、プログラムの観察可能な動作が変更されます。これは、標準では望ましくなく、許可されていません。

于 2014-09-24T09:38:27.393 に答える