#cpp #ссылки #copy #move
Есть класс 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 конструктор копирования.
Ответы
Ответ 1
Потому что param внутри wrapper является локальной переменной, а значит, имеет имя, адрес... - словом, является lvalue. Чтобы он рассматривался при передаче в функцию rvalue как rvalue, нужно использовать прямую передачу (perfect forwarding) forward: tmp = std::forward(param); Ответ 2
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() ); 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 Ответ 3
Во-первых, о каком конструкторе вообще вы ведете речь, если у вас в коде вызывается оператор присваивания? Во-вторых, не вдаваясь в формальности: для именованного объекта никогда само по себе не будет выполняться move. param - это имя. Значит какой-то move исключено, пока вы явно не запросите его через std::move или std::forward. В данном контексте язык интересует не тот факт, что param имеет тип ArgClass&&, а тот факт, что выражение param является lvalue. Для lvalue будет вызван копирующий оператор присваивания, а не перемещающий.
Комментариев нет:
Отправить комментарий