0

I know has been asked several times, I read several tutorial about it but still I can't figure out an answer.

Let's have the typical class

class Foo
{
public:
   Foo()                : i_(0) { std::cout <<  "Foo Empty ctor" << std::endl; }
   Foo(const Foo& foo)  : i_(foo.i_) { std::cout <<  "Foo Copy ctor" << std::endl; }
   Foo(int i)           : i_(i)  { std::cout <<  "Foo Param ctor" << std::endl; }
   Foo(Foo&& foo)       : i_(foo.i_) { foo.i_ = 0; std::cout <<  "Foo Move ctor" << std::endl; }
   Foo(const Foo&& foo) : i_(foo.i_) { std::cout <<  "Foo const Move ctor" << std::endl; }

   ~Foo() { std::cout << "Foo " << i_ << " destroyed\n"; }

   Foo& operator=(Foo&& other)
   {
      i_ = std::move(other.i_);
      std::cout << "Foo Move assigned\n";
      return *this;
   }
private:
   int i_;
};

Foo getFoo()
{
   return Foo(99);
}


int main()
{
   Foo f1;
   Foo f2(5);
   Foo f3(f2);
   Foo f4(Foo(4));
   Foo f5(std::move(Foo(6));
   Foo f6 = getFoo();
   Foo f7 = Foo(1);
}

Output

// f1
Foo Empty ctor

// f2 
Foo Param ctor

// f3
Foo Copy ctor

// f4 - copy elision
Foo Param ctor

// f5
Foo Param ctor
Foo Move ctor

// f6 - copy elision
Foo Param ctor

// f7 - copy elision
Foo Param ctor

So, except when I call explicitly the move operator, the copy elision optimization do the same work even if do not declare move semantics.

1 - In case when we are not dealing with pointers and I'm not going to call an explicit std::move (so I suppose an implicit call like in the examples) does it have sense declaring move semantics if the copy elision do the same without is?

2 - Does have generally sense declaring move semantics for small classes with simple data? Does it have real advantages?

Moia
  • 2,216
  • 1
  • 12
  • 34
  • 1
    **1** You need move constructor for something like `Foo f3(std::move(f2));` - for the case where you do want to explicitly move a named non-temporary to another place (e.g. to store it in a `vector`). Copy elision won't help you here. Another use case is for classes (like `unique_ptr`) that must be movable but not copyable. – Igor Tandetnik Mar 27 '18 at 22:03
  • 1
    **2** No, move semantics don't help for simple classes, where move and copy are equally cheap (or equally expensive). It only helps with classes that are much cheaper to move than to copy - classes like `std::vector` that, on move, can simply steal ownership of the existing resource managed by the temporary, but on copy have to clone that resource. And, again, there are classes that are move-only. – Igor Tandetnik Mar 27 '18 at 22:05
  • @IgorTandetnik if those simple classes are used in much complex classes whose could have reasonably implement move semantic, then should be useful to have in the simple ones too right? – Moia Mar 27 '18 at 22:29
  • You do declare move semantics for `Foo`, by providing a move-constructor. It's not clear what you mean by saying that you didn't declare move semantics – M.M Mar 27 '18 at 22:39
  • I meant if I remove the move semantics and re-run the code, the output it still the same due to the copy elision – Moia Mar 27 '18 at 22:40
  • Not really. If for class `X` move and copy would do the same thing, then `class Y { X x; std::vector movable; };` can just copy `x` in its move constructor, while moving `movable`. Oftentimes, you don't need to do anything special - in my example, implicitly-defined move constructor for `Y` would automatically do the right thing, moving members that can be moved and copying those that can't. – Igor Tandetnik Mar 27 '18 at 22:43
  • @Moia if you delete the move-constructors then the output is different (you will not see "Foo Move ctor") – M.M Mar 27 '18 at 22:45
  • @M.M My bad, I was talking about 4-6-7 where copy elision do the work instead of move semantic – Moia Mar 27 '18 at 22:49
  • @IgorTandetnik thank you, I understand your answer but it make me more confuse about when I actually could need those for a non-pointer scenario :( – Moia Mar 27 '18 at 22:54
  • 2
    Well, if you use proper RAII classes to manage your resources, then chances are high you do not in fact need to implement any copy- or move-constructors manually. See also: [Rule of zero](http://en.cppreference.com/w/cpp/language/rule_of_three#Rule_of_zero) – Igor Tandetnik Mar 27 '18 at 23:35

0 Answers0