2

Having code

string one = "one";
string two{ one };
string three{ move(one)};
cout << one  << endl;

this prints "". It is due to fact that moving constructor for string three is invoked and stole its argument value. However, how come variable one is modified?

When we pass move(one) rvalue is returned. And move constructor steals resource from THAT rvalue, the variable one is not passed as reference or pointer there. So how come behavior like this happen?

thanks for answers

jejjejd
  • 721
  • 1
  • 8
  • 18
  • 7
    `move(one)` gives you an rvalue *reference*. – juanchopanza Nov 04 '17 at 19:13
  • 1
    *"the variable one is not passed as reference or pointer there. "* - Yes it is. `std::move()` does not create a temporary it performs a type cast. So THAT rvalue is `one` cast from lvalue to rvalue reference – Galik Nov 04 '17 at 19:14
  • Possible duplicate of https://stackoverflow.com/questions/7510182/how-does-stdmove-transfer-values-into-rvalues – Galik Nov 04 '17 at 19:15
  • How would the constructor know you keep around a reference to one? You explicitely specify with move(one) that you pass the ownership to the constructor, so the behaviour is legal – Norbert Lange Nov 04 '17 at 19:17
  • 1
    To be clear, `std::move(arg)` is just a cast. It's the constructor of `std::string` that alters the argument. This is precisely what `std::move` is for -- avoiding have to make a copy of the string's data. – David Schwartz Nov 04 '17 at 19:21
  • I'm actually surprised that the string is empty. I would have thought the short string optimization would perhaps result in the characters being copied rather than a pointer being reassigned. Maybe there is some unnecessary housekeeping being done on the moved from string. Ok, I just checked; Seems like it does leave most of the short string in place on MSVC, it just places a null character in the first position. – wally Nov 04 '17 at 19:30
  • What would be the reason to invent move semantics when it would create a copy like you think it should? –  Nov 04 '17 at 19:31
  • You should read that : [C++ Rvalue Reference explained](http://thbecker.net/articles/rvalue_references/section_01.html) – HatsuPointerKun Nov 04 '17 at 19:51

3 Answers3

2

std::move() does not actually alter its argument

... but it does let code taking its output "cannibalize" the argument. Thus your three string takes away one's buffer, leaving it empty.

You can also read this detailed explanation regarding std::move() here on the site (answering the question "what is std::move() and when should it be used").

einpoklum
  • 118,144
  • 57
  • 340
  • 684
1

In the title, you ask:

Why is std::move( arg ) altering arg argument?

std::move(arg) does not alter the contents of arg. It simply cast arg to an rvalue refence.

The call to the constructor of std::string with

string three{ move(one)};

alters the contents of one since it calls the "move" constructor.

That is the desired behavior. You are asking the compiler to "move" the data from one to three. The intention is to cannibalize the contents of one and to leave it in an empty state. Of course, the notion of empty is compiler specific.

You can read more about rvalue references, move constructors and move assignment operators at http://www.stroustrup.com/C++11FAQ.html#rval.

Re:

When we pass move(one) rvalue is returned.

That is not correct. move(one) returns an rvalue reference.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

When we pass move(one) rvalue is returned. And move constructor steals resource from THAT rvalue, the variable one is not passed as reference or pointer there.

"THAT rvalue" is the one object. So three steals the data from one.

In this line:

string three{move(one)};

three receives an rvalue reference to one, so when it steals the data from that rvalue reference, it steals the data from one, since the rvalue reference is the one object.

Since one has a name and you can take its address, it is not an rvalue reference. Using std::move() you cast it to an rvalue reference, so that you can move from it. After you move from it, its state is unspecified. For an std::string this means that you can't know what it contains after moving from it. It could contain anything. It is still a valid std::string after the move, but its contents are unknown. In your particular compiler/library, it just so happens that the new state is an empty string.

Nikos C.
  • 50,738
  • 9
  • 71
  • 96