Имею такой код
#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
- деструктор.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Для QPushButton задаю иконку с 3мя установленными картинками: Normal, Disabled, ActiveПосле сборки и запуска на linux, всё отрабатывает как ожидается - при наведении...
Первая итерация срабатывает превосходно, ввожу строку - получаю ответ, а второй раз как бы "зависает" - те
"C++ Windows" миллион исходящих TCP-соединений как обойти ограничени 65535 ?
C++ как подключить к программе несколько ipЧто бы расширить локальные порты 2*65535