4

I've been trying to fully understand move semantics, but I have one question, as different examples show different things. Say we have a class Foo that has a string member str_. To define the move constructor, should I define it like this:

Foo(Foo&& foo) : str_(foo.str_) { }

or this:

Foo(Foo&& foo) : str_(std::move(foo.str_)) { }

Also, would I need to set the members of the object i am moving from to a blank value? How would I do so without constructing another string, essentially nullifying the expense saved by using a move constructor in the first place?

Krystian S
  • 1,586
  • 7
  • 23
  • 1
    *set the members to a blank value?* What do you mean? You're setting the value of `str_` by moving `foo.str_` into it. – DeiDei Jun 10 '18 at 18:20
  • @Rakete1111 The string is only an example, what if it was a very large object? – Krystian S Jun 10 '18 at 18:21
  • @DeiDei Sorry, I worded that poorly. I am referring to the members of the object I am moving from. – Krystian S Jun 10 '18 at 18:23
  • This question has some good explanations about r-value references. https://stackoverflow.com/questions/35314093/passing-int-to-fint – Galik Jun 10 '18 at 20:48

2 Answers2

1

You should use the second approach.

You do not have to do anything to the string you move from, because this is handled by string's move constructor. The latter is invoked by the move() call.

The same goes for your own classes, anything you want to move() should have a move constructor. For instance, if your class has a pointer member, your move constructor could/should assign nullptr to that member in the object you move() from.

Sid S
  • 6,037
  • 2
  • 18
  • 24
0

In a move constructor (or move assignment operator), you need to use std::move() when moving individual members (or at least, on non-POD members, since "moving" a POD type is the same as copying it).

In Foo&& foo, foo is an rvalue reference, but foo.str_ is not. If you call str_(foo.str_) in your move constructor (or str_ = foo.str_ in your move assignment operator), it will call the string's copy constructor (or copy assignment operator). So you need to cast foo.str_ to an rvalue reference via std::move() in order to invoke the string's move constructor (or move assignment operator).

A move constructor (and move assignment operator) is responsible for leaving the moved-from object in an unspecified but valid state, so its destructor will not fail. In the case of moving the foo.str_ member, the string's move constructor (or move assignment operator) will reset foo.str_ to an empty string for you, you do not need to reset it manually.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you! So I should use move as long as the member type has a move constructor? – Krystian S Jun 10 '18 at 19:17
  • 2
    Are you sure `std::string` in case of short string optimization, it sounds acceptable that a simple copy occurs. – JVApen Jun 10 '18 at 20:10
  • 4
    https://en.cppreference.com/w/cpp/string/basic_string/basic_string indicates valid but unspecified state. So to have it empty, you have to call `clear()` – JVApen Jun 10 '18 at 20:15
  • 1
    `foo` is declared as an *r-value reference* but that only determines what it can bind to. Once it is bound it is a normal *l-value reference* (because it has a name). That is why you need to re-apply `std::move`. – Galik Jun 10 '18 at 20:41