Теперь, когда на него дан ответ: не пытайтесь читать этот вопрос, он немного длинен и, вероятно, не стоит вашего времени. В моем коде были ошибки, и по этой причине конструктор перемещения не был вызван. Проверьте ответы для деталей. Помните, что RVO и NRVO (оптимизация именованных возвращаемых значений) могут учитывать вызовы, которые не происходят так, как вы ожидаете.
Я ожидаю, что ctor перемещения будет вызываться для этой строки, но вместо этого вызывается копия ctor:
Ding d3 = d1 + d2;
Класс Ding имеет пользовательский ctor перемещения и оператор + перегрузка. Причина, по которой я ожидаю вызова ctor-перемещения, заключается в том, что оператор + возвращает временный объект, ссылку на значение, так что может произойти оптимизация перемещения.
Все, что я пишу здесь, может быть неправильным, так как я новичок в 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";
}
Вызовы компилятора (на Win7) для VC2010 Express и MinGW (GCC 4.6) следующие:
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;
?
Я знаю, что есть другие вопросы относительно того, почему не вызывают вызовы ctors, но я не могу сопоставить их ответы с этим делом.
Обновление
Я изменил программу в соответствии с комментариями Дэвида Родригеса:
--- 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;
}
};
Затем я перекомпилировал программу, используя вызовы компилятора, упомянутые выше, и получил вывод, где была удалена одна копия (copy for: jajajanein
). Затем я попробовал следующую строку:
g++ -std=c++0x -fno-elide-constructors move-sem.cpp -o move-gcc.exe
И тата! Теперь я вижу движение ctor на работе! ... Но я думаю, что есть еще одна ошибка, вывод этой новой 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:
Второе обновление
Я заменил плохой 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
.