24

このクラスがあるとします。

class X {
public:
    explicit X (char* c) { cout<<"ctor"<<endl; init(c); };
    X (X& lv)  { cout<<"copy"<<endl;  init(lv.c_); };
    X (X&& rv) { cout<<"move"<<endl;  c_ = rv.c_; rv.c_ = nullptr; };

    const char* c() { return c_; };

private:
    void init(char *c) { c_ = new char[strlen(c)+1]; strcpy(c_, c); };
    char* c_;

};

そしてこのサンプルの使用法:

X x("test");
cout << x.c() << endl;
X y(x);
cout << y.c() << endl;
X z( X("test") );
cout << z.c() << endl;

出力は次のとおりです。

ctor
test
copy
test
ctor   <-- why not move?
test

デフォルト設定でVS2010を使用しています。最後のオブジェクト(z)が移動構築されることを期待しますが、そうではありません!使用すると、予想どおりX z( move(X("test")) );、出力の最後の行はです。ctor move test(N)RVOの場合ですか?

Q:move-ctorは標準に従って呼び出されるべきですか?もしそうなら、なぜそれは呼ばれないのですか?

4

3 に答える 3

30

表示されているのはコピーの省略です。これにより、コンパイラは、コピー/移動先のターゲットに一時を直接構築して、コピー(または移動)コンストラクタ/デストラクタのペアを削除できます。コンパイラがコピーの省略を適用できる状況は、C++11標準の§12.8.32で指定されています。

特定の基準が満たされると、オブジェクトのコピー/移動コンストラクタおよび/またはデストラクタに副作用がある場合でも、実装はクラスオブジェクトのコピー/移動構築を省略できます。このような場合、実装は、省略されたコピー/移動操作のソースとターゲットを、同じオブジェクトを参照する2つの異なる方法として扱い、そのオブジェクトの破棄は、2つのオブジェクトがあったはずの時間の後半に発生します。最適化せずに破壊されました。コピーの省略と呼ばれるこのコピー/移動操作の省略は、次の状況で許可されます(複数のコピーを削除するために組み合わせることができます)。


  • クラスreturn型の関数のreturnステートメントで、式が関数return型と同じcv-unqualified型の不揮発性自動オブジェクトの名前である場合、
    コピー/移動操作は、
    関数の戻り値に直接自動オブジェクト
  • throw-expressionで、オペランドが不揮発性の自動オブジェクトの名前であり、そのスコープが最も内側の囲んでいるtry-block(存在する場合)の終わりを超えない場合、オペランドから例外オブジェクト(15.1)は、自動オブジェクトを例外オブジェクトに直接構築することで省略できます。
  • 参照(12.2)にバインドされていない一時クラスオブジェクトが同じcv-unqualifiedタイプのクラスオブジェクトにコピー/移動される場合、一時オブジェクトをターゲットに直接構築することにより、コピー/移動操作を省略できます。
    省略されたコピー/移動の
  • 例外ハンドラの例外宣言(15節)
    が例外オブジェクト(15.1)と同じタイプのオブジェクト(cv-qualificationを
    除く)を宣言する場合、例外宣言をエイリアスとして扱うことにより、コピー/移動操作を省略できます。例外
    宣言によって宣言されたオブジェクトのコンストラクタとデストラクタの実行を除いて、プログラムの意味が変更されない場合は、例外オブジェクトの場合
于 2012-10-27T11:15:36.827 に答える
3

3 番目のctorコード行で得られる出力は、一時オブジェクトの構築に関するものです。その後、実際、一時変数は新しい変数に移動されますz。このような状況では、コンパイラはコピー/移動を省略することを選択する可能性があり、それが行われたようです。

規格には次のように記載されています。

(§12.8/31) 特定の基準が満たされる場合、オブジェクトのコピー/移動コンストラクタおよび/またはデストラクタに副作用がある場合でも、実装はクラス オブジェクトのコピー/移動構築を省略することができます。[...] コピー省略と呼ばれるこのコピー/移動操作の省略は、次の状況で許可されます (複数のコピーを排除するために組み合わせることができます):
[...]
- バインドされていない一時クラス オブジェクトの場合参照 (12.2) へのコピー/移動は、同じ cv 非修飾型のクラス オブジェクトにコピー/移動されます。コピー/移動操作は、省略されたコピー/移動のターゲットに一時オブジェクトを直接構築することで省略できます
[... ]

重要な条件の 1 つは、ソース オブジェクトと宛先が同じ型であることです (cv 修飾、つまり のようなものは別としてconst)。

したがって、ムーブ コンストラクターを強制的に呼び出す 1 つの方法は、オブジェクトの初期化と暗黙的な型変換を組み合わせることです。

#include <iostream>

struct B
{};

struct A
{
  A() {}
  A(A&& a) {
    std::cout << "move" << std::endl;
  }
  A(B&& b) {
    std::cout << "move from B" << std::endl;
  }
};


int main()
{
  A a1 = A(); // move elided
  A a2 = B(); // move not elided because of type conversion
  return 0;
}
于 2012-10-27T11:27:20.410 に答える
0

X's char*コンストラクターをX("test")明示的に呼び出しています。

したがって、それは印刷していますctor

于 2012-10-27T11:16:07.210 に答える