In c++11 why not right to use moved variable after std::move?

Deadly 提交于 2020-08-25 02:35:50

问题


As std::move(v) is merely a cast like

static_cast<T&&>(v)

where T is the type of v. But why the moved variable not a reference of the original object?

For example, when o1 is "moved" to o2, why can't we access the str_ of o2 through o1.str_ any more?

#include <string>
#include <iostream>

struct MyClass {
  std::string str_;
  MyClass(MyClass const &) = delete;
  MyClass &operator=(MyClass const &) = delete;
  MyClass(MyClass &&o) : str_(std::move(o.str_)) {}
  MyClass(std::string const &str) : str_(str) {}
};

int main(void) {
  MyClass o1 = MyClass("o1");
  MyClass o2(std::move(o1));
    std::cout << "o1: " << o1.str_ << "\n"
        << "o2: " << o2.str_ << std::endl;
  return 0;
}

output:

o1: 
o2: o1

update:

It seems that when I change

MyClass(MyClass &&o) : str_(std::move(o.str_)) {}

to

MyClass(MyClass &&o) : str_(o.str_) {}

the output would be:

o1: o1
o2: o1

So the root cause is the "std::string" move? But why this makes different?


回答1:


Let's forget about your class for now, and just take an std::string as an example.

std::string s1{"Hello, World!"};  // 1
std::string s2{s1};               // 2
std::string s3{std::move(s1)};    // 3

On line 1 you've constructed an std::string object. On line 2 you're copying s1 to s2. Now both s1 and s2 will contain their own copies of the string "Hello, World!". This copy is being done by the copy constructor of std::string (or std::basic_string<char>) which does not modify the argument.

basic_string(basic_string const& other);

On line 3 you're moving the contents of s1 to s3. In order to do this, you first cast s1 to std::string&& (this is what std::move does). Because of this cast, the call will now match std::string's move constructor, instead of the copy constructor like the previous line.

basic_string(basic_string&& other) noexcept;

This constructor, because it's always called with an rvalue instance of the string, has license to steal resources from the argument. So internally the constructor will simply copy over some pointers to the memory that was allocated by s1 to store the string, and set the state of the s1 instance such that it is now empty. Thus s3 now owns the string.

The same things are happening when you move the string data member within your class instance. That's why the moved from std::string object appears empty when you print it.

The operations performed are different if the std::string implementation uses small string optimization, but that is just an implementation detail. Conceptually, both cases work as described above.




回答2:


You are quite right that std::move just casts its argument to rvalue-reference.

But any function receiving such an rvalue-reference has explicit license to pillage it in order to more efficiently do its work, it just must leave it in some arbitrary valid state.

You can certainly keep using it though, just be aware how little "some arbitrary valid state" guarantees.

This pillaging of the moved object makes move-semantics move-semantics, and was explicit goal.




回答3:


Because the implementation for most move constructors really moves the data from one object to another and invalidates the data on the first object.

This is why it is called moving and not copying.



来源:https://stackoverflow.com/questions/25341407/in-c11-why-not-right-to-use-moved-variable-after-stdmove

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!