1
#include <iostream>

using namespace std;

struct Item
{
  Item () {cout << "Item constructor called." << endl;}
  ~Item () {cout << "Item destructor called." << endl;}
  Item (const Item& item): x(item.x) {cout << "Item copy constructor called." << endl;}
  Item (Item&& item) : x(std::move(item.x)) {cout << "Item move constructor called." << endl;}
  Item& operator=(const Item& item) { x= item.x; cout << "Item assignment operator called." << endl; return *this;}
  Item& operator=(Item&& item) { x= std::move(item.x); cout << "Item move assignment operator called." << endl; return *this;}

  int x = 0;
};

struct ItemHandler
{
    Item getItem() 
    {
      cout << "getItem called."<< endl; 
      return item;
    }

    Item item{}; 
};

int main()
{
  ItemHandler ih;  
  cout << "trying move assignment" << endl;
  Item&& it = ih.getItem();
}

I was expecting that since ih.getItem() will create a copy then move assign it to it. But Here is the output that I got:

Item constructor called.
trying move assignment
getItem called.
Item copy constructor called.
Item destructor called.
Item destructor called.
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
jko
  • 133
  • 1
  • 7
  • 14

1 Answers1

0
Item getItem() 
{
    return item;
}

is equivalent to

Item getItem() 
{
    return this->item;
}

this->item is a data member lvalue. The compiler will not move it for you unless you are explicit about it. Lvalues are implicitly moved only for some special cases.


The correct way to solve this problem is to provide ref-qualified versions of getItem():

Item& getItem() &   
{
    return this->item;
}

const Item& getItem() const&
{
    return this->item;
}

Item getItem() &&
{
    return std::move(this->item);
}

And then use std::move in your main:

int main()
{
    ItemHandler ih;  
    Item it = std::move(ih).getItem();
}
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Why doesn't `Item &&it = ih.getItem()` call the `&&` version of `getItem` ? –  Mar 05 '18 at 11:43
  • 1
    @Tobias: because `ih` is an lvalue. The type of `it` doesn't matter at all here, only the type of `ih` does. – Vittorio Romeo Mar 05 '18 at 11:44
  • @VittorioRomeo as I understand, even if this->item is an lvalue getItem() creates a temporary copy of that lvalue before returning. Which means it is an rvalue right? – jko Mar 05 '18 at 12:15
  • 1
    @jko: correct - the returned temporary is an rvalue. The temporary however is construct from `this->item`, which is an lvalue. The copy constructor is invoked instead of the move constructor. – Vittorio Romeo Mar 05 '18 at 12:17
  • @VittorioRomeo I think I understand that part but after returning the rvalue from ih.getItem(), I am confused with why doesn't `Item&&` `it` `=` call the move assignment operator? – jko Mar 05 '18 at 12:36
  • 1
    @jko: because `Item&&` is not an object, it is a reference. You are not constructing/assigning to anything – Vittorio Romeo Mar 05 '18 at 12:48
  • i see. so that means after returning rvalue, it is already referenced by &&it? – jko Mar 05 '18 at 13:42