Конструктор копирования не вызывается при возврате по значению

Я играл с конструкторами C ++. Вот мой код:

#include <iostream>
using namespace std;

class ArrayWrapper
{
public:
// default constructor produces a moderately sized array
ArrayWrapper ()
: _p_vals( new int[ 64 ] )
, _size( 64 )
{
cout << "Default constructor: " << this << endl;
}

explicit ArrayWrapper (int n)
: _p_vals( new int[ n ] )
, _size( n )
{
cout << "Constructor: " << this << endl;
}

// move constructor
ArrayWrapper (ArrayWrapper&& other)
: _p_vals( other._p_vals  )
, _size( other._size )
{
cout << "Move constructor: " << this << endl;
cout << "Move from: " << &other << endl;
other._p_vals = NULL;
other._size = 0;
}

// copy constructor
ArrayWrapper (const ArrayWrapper& other)
: _p_vals( new int[ other._size  ] )
, _size( other._size )
{
cout << "Copy constructor: " << this << endl;
for ( int i = 0; i < _size; ++i )
{
_p_vals[ i ] = other._p_vals[ i ];
}
}
~ArrayWrapper ()
{
cout << "Destructor: " << this << endl;
delete [] _p_vals;
}

public:
int *_p_vals;
int _size;
};

ArrayWrapper foo() {
ArrayWrapper a(7);
cout << "Temp object created!" << endl;
return a;
}int main() {
ArrayWrapper b(foo());
cout << "Finish!" << endl;
}

Выход:

Constructor: 0x7fff5d97bb60
Temp object created!
Destructor: 0x7fff5d97bb60
Move constructor: 0x7fff5d97bbd0
Move from: 0x7fff5d97bbc0
Destructor: 0x7fff5d97bbc0
Finish!
Destructor: 0x7fff5d97bbd0

Первые три строки указывают, что локальная переменная в функции foo () создается с помощью конструктора и уничтожается при возврате функции foo (). 4-я строка указывает, что b построен с использованием конструктора перемещения. Но следующие две строки являются наиболее запутанными: у меня теперь есть новый адрес, который отличается от локальной переменной «a» в foo (), которую я использовал для вызова конструктора перемещения. Когда конструктор копирования заканчивается, ссылка на rvalue исчезает, и вызывается деструктор. Но почему нет конструктора копирования для 0x7fff5d97bbc0? Другими словами, откуда берется 0x7fff5d97bbc0 и как он построен? Просто запрограммировано, что есть еще один вызываемый деструктор, чем вызываемый конструктор.

У меня есть ощущение, что это связано с копией elision. Таким образом, я изменил строку возврата в foo () следующим образом:

return std::move(a);

И вывод:

Constructor: 0x7fff55a7ab58
Temp object created!
Copy constructor: 0x7fff55a7abc0
Destructor: 0x7fff55a7ab58
Move constructor: 0x7fff55a7abd0
Move from: 0x7fff55a7abc0
Destructor: 0x7fff55a7abc0
Finish!
Destructor: 0x7fff55a7abd0

Теперь это, наконец, имело некоторый смысл: в третьей строке показано, что конструктор копирования вызывается до уничтожения «а». Это означает, что при возврате по значению он фактически копировал значение в возвращаемое значение перед уничтожением временной переменной.

Но я все еще запутался в исходной программе (без std :: move ()), потому что, если это действительно вызвано копией elision, не должен ли адрес возвращаемого значения foo () совпадать с локальной переменной «a» ? Теперь, когда он отличается, что означает, что он находится в совершенно отличной позиции в памяти от «а», тогда почему он не вызвал конструктор копирования?

Надеюсь, мой вопрос ясен и понятен.

——————————————————————————-

Изменить: компилятор, который я использовал, был clang ++ с флагом -fno-elide-constructors.

9

Решение

Какой у тебя компилятор, лязг без std::move:

Constructor: 0x7fff0b8e3b80
Temp object created!
Finish!
Destructor: 0x7fff0b8e3b80

с std::move:

Constructor: 0x7fffca87eef0
Temp object created!
Move constructor: 0x7fffca87ef30
Move from: 0x7fffca87eef0
Destructor: 0x7fffca87eef0
Finish!
Destructor: 0x7fffca87ef30

Эти два результата гораздо более логичны, чем ваш, так что опять же, каков ваш компилятор?

Редактировать: Это похоже на ошибку с флагом -fno-elide-constructors.

добавление члена int после двух исходных членов приводит к тому же результату, но если int является первым, повреждение памяти! И версия без коррупции заканчивается значением nullptr в главном ArrayWrapper. Посмотрите журнал «и удалите», чтобы поймать ошибочное поведение.

http://coliru.stacked-crooked.com/a/f388c504b442b71d <- после, хорошо

http://coliru.stacked-crooked.com/a/9beced1d5a2aa6e4 <- до, коррупционная свалка

3

Другие решения

Похоже, вы получаете много копий, с g ++ 4.8 я получаю следующий вывод:

Constructor: 0x7fff88925df0
Temp object created!
Finish!
Destructor: 0x7fff88925df0

если я добавлю: -fno-elide-constructors, то получу:

Constructor: 0x7fff1bd329b0   for this line: ArrayWrapper a(7);
Temp object created!
Move constructor: 0x7fff1bd329f0  for temporary
Move from: 0x7fff1bd329b0  moving from this : ArrayWrapper a(7)
Destructor: 0x7fff1bd329b0   foo is ending so destroy: ArrayWrapper a(7);
Move constructor: 0x7fff1bd329e0  for ArrayWrapper b - it is being created
Move from: 0x7fff1bd329f0  moving from temporary
Destructor: 0x7fff1bd329f0 destroying temporary
Finish!
Destructor: 0x7fff1bd329e0  destroy object b

http://coliru.stacked-crooked.com/a/377959ae1e93cdc9

0