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

131
08 июля 2019, 08:30

Есть класс

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 конструктор копирования.

Answer 1

Потому что param внутри wrapper является локальной переменной, а значит, имеет имя, адрес... - словом, является lvalue.

Чтобы он рассматривался при передаче в функцию rvalue как rvalue, нужно использовать прямую передачу (perfect forwarding) forward:

tmp = std::forward<Type>(param);
Answer 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>( 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
Answer 3

Во-первых, о каком конструкторе вообще вы ведете речь, если у вас в коде вызывается оператор присваивания?

Во-вторых, не вдаваясь в формальности: для именованного объекта никогда само по себе не будет выполняться move. param - это имя. Значит какой-то move исключено, по вы явно не запросите его через std::move или std::forward.

В данном контексте язык интересует не тот факт, что param имеет тип ArgClass&&, а тот факт, что выражение param является lvalue. Для lvalue будет вызван копирующий оператор присваивания, а не перемещающий.

READ ALSO
работа с enum class

работа с enum class

у меня есть два enum class

149
Удаление на промежутке в multiset

Удаление на промежутке в multiset

Как в multiset удалять элементы на промежутке [first, last] (рамки промежутка вводятся с консоли)? first и last - условные позиции элементов, если бы они имели...

161
Аллокаторы памяти

Аллокаторы памяти

Не так давно услышал о том, что существует способ управлять памятью самому, а не использовать, например, new и deleteМожет кто-нибудь сможет осветить...

138
Получение элемента из QTableWidget

Получение элемента из QTableWidget

QTableWidgetУсловие: Создать квадратную матрицуМатрица должна содержать слова из 4х букв английского алфавита

158