0

このサンプルの場合:

// test.cpp

#include <iostream>
#include <vector>
#include <utility>

using namespace std;

class mystring : public string { public:
    mystring() = default;

    mystring(const char* c) : string(c) {}

    mystring(mystring& m) : string(m) { cout << "Reference" << endl; }
    mystring(mystring const & m) : string(m) { cout << "Const reference" << endl; }
    mystring(mystring&& m) : string(move(m)) { cout << "Move" << endl; }
};

int main() {
    mystring a;

    vector<mystring> v{ a };
}

出力は次のとおりです。

$ g++ --version | grep "g++"
g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
$ g++ -std=c++11 -fno-elide-constructors test.cpp
$ ./a.out
Reference
Move
Const reference

しかし、vをr値で初期化すると:

vector<mystring> v{"hello"};

出力は次のとおりです。

$ g++ -std=c++11 -fno-elide-constructors test.cpp
$ ./a.out
Const reference

つまり、コピーはありません。r値の場合:

vector <mystring> v{mystring()};

出力:

$ g++ -std=c++11 -fno-elide-constructors test.cpp
$ ./a.out
Move
Move
Const reference

私は2つのことを理解していません:

  • 生の文字列がない場合、2番目の移動(最初のコピー/移動の前)が実行されるのはなぜですか?
  • なぜ生の文字列では、「mystring」のコピーが実行されないのですか?
4

2 に答える 2

2

NB生の文字列は、C ++ 11で何か違うことを意味します。あなたが話しているのであれば、文字列リテラルを意味すると思います"hello"

  • なぜ生の文字列では、「mystring」のコピーが実行されないのですか?

え?コピーが1つあるので、印刷されConst Referenceます。

vector<mystring> v{"hello"};

braced-init-listは、コンストラクターを使用して構築std::initializer_list<mystring>された単一の要素でコンストラクターパラメーターを初期化しmystring(const char* c)、次にその要素をコンストラクターを使用してベクトルにコピーします。mystring(const mystring&)これにより、が出力されConst Referenceます。ものすごく単純。

  • 生の文字列がない場合、2番目の移動(最初のコピー/移動の前)が実行されるのはなぜですか?

最初の動きの前に2番目の動きはどのようになりますか?!:)

これは、がどのように構築されるかについての実装の詳細でありinitializer_list、余分な動きがなくなるため、通常は問題になりません。

vector <mystring> v{mystring()};

これによりmystring、デフォルトのコンストラクターを使用して一時的なものが作成され、次に、引数として一時的なものをstd::initializer_list<mystring>使用してコンストラクターを使用して構築された単一の要素で作成されます。mystring(mystring&&)その動きの建設プリントMove。内部で行われる別の動きがあり、Move再度印刷します。次に、initializer_list要素が前と同じようにベクトルにコピーされ、印刷されConst Referenceます。

あなたのコメントに答えるには:

表示される余分な移動は最初のケースでも発生しますが、その場合、移動されるタイプconst char*は印刷されないためMove、ステートメントstd::vector<mystring>{ expr };は次のように考えることができます。

auto tmp = expr;
mystring tmparray[] = { std::move(tmp) };
std::initializer_list<mystring> init_list( tmparray, 1 };
std::vector<mystring> v(init_list);

expris a(非定数値)の場合、プリントを作成してからtmpプリントReferenceを作成し、ベクタープリントtmparrayMoveコピーします。tmparray[0]Const Reference

expr"hello"要素を作成すると何も出力されず、何も出力しない呼び出しをtmp作成してから、ベクターにコピーすると印刷されます。tmparraymystring(const char*)tmparray[0]Const Reference

プリントexprmystring()作成し、次にプリントを作成し、次にベクタープリントにコピーする場合tmpMovetmparrayMovetmparray[0]Const Reference

次のような2つの要素のbraced-init-listを使用すると、次のよう{ expr1, expr2 }になります。

auto tmp1 = expr1;
auto tmp2 = expr2;
mystring tmparray[] = { std::move(tmp1), std::move(tmp2) };
std::initializer_list<mystring> init_list( tmparray, 2 };
std::vector<mystring> v(init_list);

したがって、使用する場合は{ "hello", mystring() }、の要素によって1回印刷され、1回Move印刷tmp2され、 2回印刷されます。MovetmparrayConst Referencev

もちろん、通常はこれらすべてが排除されるため、不要なコピーや移動はありません。 -fno-elide-constructors何が起こるかを知ることを除いて、実際には役に立ちませんが、実際には起こりません!

于 2012-12-14T13:32:57.403 に答える
0

移動コンストラクターに。というラベルを正しく付けていませんnoexcept

于 2012-12-14T13:23:53.363 に答える