回答が得られたので、この質問を読むのを気にしないでください。少し長く、おそらく時間の価値はありません。コードにバグがあり、それが原因で move コンストラクターが呼び出されませんでした。詳細については、回答を確認してください。RVO および NRVO (名前付き戻り値の最適化) は、期待どおりに発生しない呼び出しを説明する可能性があることに注意してください。
この行で move ctor が呼び出されることを期待していますが、代わりに copy ctor が呼び出されます。
Ding d3 = d1 + d2;
Ding クラスには、ユーザー定義の move ctor と on operator+ オーバーロードがあります。move ctor が呼び出されると予想する理由は、operator+ が一時オブジェクト (rvalue 参照) を返すため、移動の最適化が行われる可能性があるからです。
私は C++ の初心者なので、ここに書いていることはすべて間違っている可能性があります。コードは次のとおりです。
// Copied and modified code from here: https://stackoverflow.com/a/3109981
#include <iostream>
#include <cstring>
struct Ding {
char* data;
Ding(const char* p) {
std::cout << " ctor for: " << p << "\n";
size_t size = strlen(p) + 1;
data = new char[size];
memcpy(data, p, size);
}
~Ding() {
std::cout << " dtor for: " << data << "\n";
delete[] data;
}
Ding(const Ding& that) {
std::cout << " copy for: " << that.data << "\n";
size_t size = strlen(that.data) + 1;
data = new char[size];
memcpy(data, that.data, size);
}
Ding(Ding&& that) {
std::cout << " MOVE for: " << that.data << "\n";
data = that.data;
that.data = nullptr;
}
Ding& operator=(Ding that) {
std::cout << " assignment: " << that.data << "\n";
std::swap(data, that.data);
return *this;
}
Ding& operator+(const Ding that) const {
std::cout << " plus for: " << that.data << "\n";
size_t len_this = strlen(this->data);
size_t len_that = strlen(that.data);
char * tmp = new char[len_this + len_that + 1];
memcpy( tmp, this->data, len_this);
memcpy(&tmp[len_this], that.data, len_that + 1);
Ding * neu = new Ding(tmp);
return *neu;
}
};
void print(Ding d) {
std::cout << " (print): " << d.data << std::endl;
}
int main(void) {
std::cout << "putting a Ding on the stack\n";
Ding d1("jajaja");
std::cout << "calling print routine\n";
print(d1);
std::cout << "putting a second Ding on the stack\n";
Ding d2("nein");
// std::cout << "calling print routine\n";
// print(d2);
std::cout << "Putting a third Ding on the stack, init from + op ...\n";
std::cout << "... so expecting so see MOVE ctor used ...\n";
Ding d3 = d1 + d2;
// std::cout << "calling print routine\n";
// print(d3);
std::cout << "End of main, dtors being called ...\n";
}
VC2010 Express および MinGW (GCC 4.6) のコンパイラ呼び出し (Win7 上) は次のとおりです。
cl /nologo /W4 /EHsc /MD move-sem.cpp
g++ -std=c++0x move-sem.cpp -o move-gcc.exe
どちらのバイナリも同じ出力を生成します (プログラムの最後に破棄する順序はありません)。
putting a Ding on the stack
ctor for: jajaja
calling print routine
copy for: jajaja
(print): jajaja
dtor for: jajaja
putting a second Ding on the stack
ctor for: nein
Putting a third Ding on the stack, init from + op ...
... so expecting so see MOVE ctor used ...
copy for: nein
plus for: nein
ctor for: jajajanein
dtor for: nein
copy for: jajajanein
End of main, dtors being called ...
dtor for: jajajanein
dtor for: nein
dtor for: jajaja
この長いテキストの後の質問を思い出してDing d3 = d1 + d2;
ください。
move ctors が呼び出されない理由については他にも質問があることは承知していますが、その答えをこのケースにマッピングすることはできません。
- この C++0x コードが move コンストラクターを呼び出さないのはなぜですか?
- このコードがコピー コンストラクターを呼び出そうとしているのはなぜですか?
- Move コンストラクターが C++0x で呼び出されない
アップデート
David Rodriguez のコメントに従って、プログラムを次のように変更しました。
--- move-sem.cpp.orig 2012-03-17 17:00:56.901570900 +0100
+++ move-sem.cpp 2012-03-17 17:01:14.016549800 +0100
@@ -36,15 +36,14 @@
return *this;
}
- Ding& operator+(const Ding that) const {
+ Ding operator+(const Ding that) const {
std::cout << " plus for: " << that.data << "\n";
size_t len_this = strlen(this->data);
size_t len_that = strlen(that.data);
char * tmp = new char[len_this + len_that + 1];
memcpy( tmp, this->data, len_this);
memcpy(&tmp[len_this], that.data, len_that + 1);
- Ding * neu = new Ding(tmp);
- return *neu;
+ return tmp;
}
};
次に、上記のコンパイラ呼び出しを使用してプログラムを再コンパイルし、1 つのコピー ( copy for: jajajanein
) が削除された出力を取得しました。次に、次の行を試しました。
g++ -std=c++0x -fno-elide-constructors move-sem.cpp -o move-gcc.exe
そしてタタ!今、私は仕事でムーブ・クターを見ています!...しかし、現在別のエラーがあると思います。その new の出力にはmove-gcc.exe
、dtor 呼び出しが一覧表示されていません。
putting a Ding on the stack
ctor for: jajaja
calling print routine
copy for: jajaja
(print): jajaja
dtor for: jajaja
putting a second Ding on the stack
ctor for: nein
Putting a third Ding on the stack, init from + op ...
... so expecting so see MOVE ctor used ...
copy for: nein
plus for: nein
ctor for: jajajanein
MOVE for: jajajanein
dtor for:
2 回目の更新
operator+
悪いものを次の(おそらく同じように悪い)コードに置き換えました。
Ding& operator+=(const Ding & rhs) {
std::cout << " op+= for: " << data << " and " << rhs.data << "\n";
size_t len_this = strlen(this->data);
size_t len_that = strlen(rhs.data);
char * buf = new char[len_this + len_that + 1];
memcpy( buf, this->data, len_this);
memcpy(&buf[len_this], rhs.data, len_that + 1);
delete[] data;
data = buf;
return *this;
}
Ding operator+(const Ding & rhs) const {
Ding temp(*this);
temp += rhs;
return temp;
}
また、デストラクタから次の行を削除したところ、プログラムが異常終了するのを防ぎました。
std::cout << " dtor for: " << data << "\n";
MSVC および を使用してコンパイルすると、移動コンストラクタが呼び出されるようになりましたg++ -std=c++0x -fno-elide-constructors
。