Страницы

Поиск по вопросам

суббота, 1 февраля 2020 г.

Почему вызывается copy вместо move?

#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 будет вызван копирующий оператор присваивания, а не перемещающий.

Комментариев нет:

Отправить комментарий