0

The following simple code shows that static_cast to r-value reference type and std::move may change their input argument (here variable inupt) in an initialization statement depending on the type of the object getting initialized. Can someone explain this behavior?

I am at least glad that static_cast and std::move behave similarly since std::move does use static_cast under the hood

#include <iostream>
#include <string>

int main() {

   {
      std::string input("hello");
      std::string&& result = static_cast<std::string&&>(input);
      std::cout << "cast case 1: input: " << input << " result: " << result << std::endl; // prints: cast case 1: input: hello result: hello
   } 

   {
      std::string input("hello");
      std::string result = static_cast<std::string&&>(input);
      std::cout << "cast case 2: input: " << input << " result: " << result << std::endl; // prints: cast case 2: input:  result: hello
   }

   {
      std::string input("hello");
      static_cast<std::string&&>(input);
      std::cout << "cast case 3: input: " << input << std::endl; // prints: cast case 3: input: hello
   }

   {
      std::string input("hello");
      std::string&& result = std::move(input);
      std::cout << "move case 1: input: " << input << " result: " << result << std::endl; 
      // prints: move case 1: input: hello result: hello
   } 

   {
      std::string input("hello");
      std::string result = std::move(input);
      std::cout << "move case 2: input: " << input << " result: " << result << std::endl; 
      // prints: move case 2: input:  result: hello
   }

   {
      std::string input("hello");
      std::move(input);
      std::cout << "move case 3: input: " << input << std::endl; 
      // prints: move case 3: input: hello
   }

}
Ahmad S
  • 68
  • 6
  • 3
    That's exactly what move construction does: potentially grab anything from the original object if it helps with optimization or is required by semantics. – Quentin Jul 24 '19 at 17:02
  • I would suggest https://stackoverflow.com/questions/3413470/what-is-stdmove-and-when-should-it-be-used?rq=1 and/or https://stackoverflow.com/questions/3106110/what-is-move-semantics?noredirect=1&lq=1 as a duplicate, because reading through there should explain all these test cases. – Max Langhof Jul 24 '19 at 17:09
  • Note that this experiment can't be used to conclude that moving has *not* taken place, because a *moved-from* `std::string` is not guaranteed to be empty. You would have to use something like a `std::unique_ptr` to know for sure. – Brian Bi Jul 24 '19 at 17:20
  • Possible duplicate of [What is move semantics?](https://stackoverflow.com/questions/3106110/what-is-move-semantics) – L. F. Jul 25 '19 at 00:48

2 Answers2

2

The behavior you are observing is not surprising and makes sense.

When you use

  std::string result = static_cast<std::string&&>(input);

the move constructor is called to initialize result. It makes sense that the move constructor will move the contents of input to result.

Contrast that with

  std::string&& result = static_cast<std::string&&>(input);

Here, result is not a new object. It's just a r-value referene to input. There is nothing here that requires the contents of input to be moved.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • This does not explain the whole story. Why doesn't **input** change after static_cast or move under under cases1 and 3? – Ahmad S Jul 24 '19 at 17:08
  • 1
    @AhmadS In case 1 you do not create a new object, you just hold an (rvalue) reference to `input` in `result`. Just as if you wrote `std::string& result = input;`. In case 3 you are confused because `std::move` does not by itself move things, it just makes them "available for being moved from". I would suggest you read a comprehensive introduction into move semantics because this is not something to learn by trial and error (although I applaud getting in there and testing things by yourself). – Max Langhof Jul 24 '19 at 17:11
  • 2
    @AhmadS because you're only juggling references, you're not constructing any object that might move-init itself. – Quentin Jul 24 '19 at 17:12
0

at least static_cast and std::move behave similarly since std::move does use static_cast

Not only similarly, but they do exactly the same thing. std::move is a static cast to an rvalue reference.

Can someone explain this behavior?

  1. result is a reference that refers to input. Binding a reference does not modify the referred object.
  2. An object is initialised. Since it is initialised from an rvalue, the move constructor is used. As per documentation of std::string, the input object is left in an unspecified but valid state, which may but might not be different from what it was before being moved from.
  3. The result of the cast is discarded. This has no side-effects, and input will not be modified.
eerorika
  • 232,697
  • 12
  • 197
  • 326