Есть класс
class ArgClass {
public:
ArgClass();
ArgClass( const ArgClass& o );
ArgClass& operator=( const ArgClass& o );
ArgClass( ArgClass&& o );
ArgClass& operator=( ArgClass&& o );
~ArgClass();
};
Шаблонная функция:
template< typename Type >
void wrapper( Type&& param )
{
ArgClass tmp;
tmp = param;
}
Почему данный код:
int main()
{
ArgClass ac;
wrapper( ArgClass() );
}
вызовет конструктор копирования в строке "tmp = param". Параметр функции wrapper имеет тип ArgClass&&, и должен вызываться move конструктор копирования.
Потому что param
внутри wrapper
является локальной переменной, а значит, имеет имя, адрес... - словом, является lvalue.
Чтобы он рассматривался при передаче в функцию rvalue как rvalue, нужно использовать прямую передачу (perfect forwarding) forward
:
tmp = std::forward<Type>(param);
https://habr.com/post/226229/
В строке tmp = param
вызовется оператор присваивания, почему конструктор копии?
При явном вызове tmp = param;
будет вызван оператор lvalue =, потому что он существует и определен, это гарантирует сохранность переданного объекта вне зависимости от того, как именно он принимается в функцию, как lvalue или rvalue.
можно его удалить ArgClass& operator=( const ArgClass& o ) = delete;
и убедиться, что компилятор пытается вызвать lvalue=
class ArgClass {
public:
ArgClass() { std::cout << "base cons" << std::endl; };
ArgClass( const ArgClass& o ) { std::cout << "lv cons" << std::endl; }; ;
ArgClass& operator=( const ArgClass& o ) { std::cout << "lv assign" << std::endl; return *this;};;
ArgClass( ArgClass&& o ) { std::cout << "rv cons" << std::endl; };;
ArgClass& operator=( ArgClass&& o ) { std::cout << "rv ass" << std::endl; return *this;};;
~ArgClass() { std::cout << "destr" << std::endl; };;
};
template< typename Type >
void wrapper( ArgClass&& param )
{
ArgClass tmp;
tmp = (param);
}
int main()
{
wrapper<ArgClass>( ArgClass() );
system("pause");
}
base cons
base cons
lv assign
destr
destr
При явном вызове перемещения, будет вызван соответственно оператор rvalue=
template< typename Type >
void wrapper( ArgClass&& param )
{
ArgClass tmp;
tmp = std::move(param);
}
base cons
base cons
rv ass
destr
destr
Для конструктора копии:
template< typename Type >
void wrapper( ArgClass&& param )
{
ArgClass tmp(param);
}
base cons
lv cons
destr
destr
И перемещения:
template< typename Type >
void wrapper( ArgClass&& param )
{
ArgClass tmp(std::move(param));
}
base cons
rv cons
destr
destr
Такое поведение возможно обусловлено тем, что мув разрушает объект и именно явный вызов мув говорит о том, что объект будет утерян, в то время как обычные вызовы гарантируют сохранность объекта
P.S. мувнуть объект можно и по обычной ссылке
template< typename Type >
void wrapper( ArgClass& param )
{
ArgClass tmp(std::move(param));
}
base cons
rv cons
destr
destr
Во-первых, о каком конструкторе вообще вы ведете речь, если у вас в коде вызывается оператор присваивания?
Во-вторых, не вдаваясь в формальности: для именованного объекта никогда само по себе не будет выполняться move. param
- это имя. Значит какой-то move исключено, по вы явно не запросите его через std::move
или std::forward
.
В данном контексте язык интересует не тот факт, что param
имеет тип ArgClass&&
, а тот факт, что выражение param
является lvalue. Для lvalue будет вызван копирующий оператор присваивания, а не перемещающий.
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Как в multiset удалять элементы на промежутке [first, last] (рамки промежутка вводятся с консоли)? first и last - условные позиции элементов, если бы они имели...
Не так давно услышал о том, что существует способ управлять памятью самому, а не использовать, например, new и deleteМожет кто-нибудь сможет осветить...
QTableWidgetУсловие: Создать квадратную матрицуМатрица должна содержать слова из 4х букв английского алфавита