Имею такой код
#include <iostream>
using namespace std;
class Unit{
int a_;
char* pch_;
public:
Unit(){ cout << "Simple constr" << endl;}
Unit(int r) : a_(r), pch_(new char[100]){ cout << "Constr " << endl;}
Unit(const Unit& ){ cout << "Constr copy " << endl;}
~Unit(){
delete [] pch_;
cout << "Destr" << endl;
}
};
int main(){
Unit a = 20;
return 0;
}
Вызывается только конструктор с параметром и естественно деструктор.
Почему не так: 1. Вызывается конструктор с параметром: Unit(20). 2. Происходит присваивание в объект который не был создан - это копирование. Вызывается конструктор копирования. Как раз rvalue можно передать по const T&. 3. Деструктор для rvalue. 4. Деструктор для a.
В "классическом" С++ (С++98) ваша инициализация копированием (copy-initialization)
Unit a = 20;
действительно концептуально означала именно
Unit a = Unit(20);
с применением конструктора конверсии Unit::Unit(int) и затем конструктора копирования Unit::Unit(const Unit &). Однако даже в "классическом" С++ компилятору было разрешено исключать формирование промежуточного временного объекта и оптимизировать код до прямой инициализации (direct-initialization)
Unit a(20);
т.е. исключать вызов конструктора копирования даже если конструктор копирования обладал побочными эффектами. Эта оптимизация называется copy elision. (Даже при выполнении copy elision наличие доступного конструктора копирования все равно требовалось.)
Начиная с С++17 в языке появилась гарантированная copy elision, при котором ваша инициализация гарантированно трактуется как
Unit a(20);
без требования наличия конструктора копирования.
Т.е. в любом случае, ожидать тут обязательного вызова конструктора копирования вы не должны (и не должны были никогда). В "классическом" С++ конструктор копирования тут мог быть вызван, но не более того.
В вашем конкретном случае инициализация копированием (copy-initialization) ведет себя идентично прямой инициализации (direct-initialization), но в общем случае существенные различия между этими формами инициализации сохраняются и в С++17. Например
struct A {
A(int) {}
};
struct B {
operator int() const { return 0; }
};
int main()
{
B b;
A a1(b); // Все в порядке
A a2 = b; // Ошибка
}
P.S. Никакого "присваивания" тут, конечно, нет и в помине.
Пусть наличие = не вводит в вас в заблуждение, форма записи Unit a = 20; называется copy initialization она полностью аналогична записи Unit a(20);, за тем исключением, что не допускает вызова explicit конструктора.
Начиная с C++11 вместо них следует использовать list initialization Unit a{20};.
Начиная с С++17 добиться вызова конструктора копирования / перемещения при создании временного объекта больше невозможно, т.е.
Unit foo(void) { return Unit{Unit{20}}; }
Unit a{Unit{foo()}};
Приведет к вызову только одного конструктора.
Потому что
Unit a = 20;
это по сути то же, что и
Unit a(20);
просто другая форма записи. Т.е. вызывается конструктор Unit(int r).
А при выходе из main - деструктор.
Сборка персонального компьютера от Artline: умный выбор для современных пользователей