What Lasts After Using Std::Move C++11

What lasts after using std::move c++11

The common mantra is that a variable that has been "moved-from" is in a valid, but unspecified state. That means that it is possible to destroy and to assign to the variable, but nothing else.

(Stepanov calls this "partially formed", I believe, which is a nice term.)


To be clear, this isn't a strict rule; rather, it is a guideline on how to think about moving: After you move from something, you shouldn't want to use the original object any more. Any attempt to do something non-trivial with the original object (other than assigning to it or destroying it) should be carefully thought about and justified.

However, in each particular case, there may be additional operations that make sense on a moved-from object, and it's possible that you may want to take advantage of those. For example:

  • The standard library containers describe preconditions for their operations; operations with no pre­conditions are fine. The only useful ones that come to mind are clear(), and perhaps swap() (but prefer assignment rather than swapping). There are other operations without preconditions, such as size(), but following the above reasoning, you shouldn't have any business inquiring after the size of an object which you just said you didn't want any more.

  • The unique_ptr<T, D> guarantees that after being moved-from, it is null, which you can exploit in a situation where ownership is taken conditionally:

    std::unique_ptr<T> resource(new T);
    std::vector<std::function<int(std::unique_ptr<T> &)> handlers = /* ... */;

    for (auto const & f : handlers)
    {
    int result = f(resource);
    if (!resource) { return result; }
    }

    A handler looks like this:

    int foo_handler(std::unique_ptr<T> & p)
    {
    if (some_condition))
    {
    another_container.remember(std::move(p));
    return another_container.state();
    }
    return 0;
    }

    It would have been possible generically to have the handler return some other kind of state that indi­cates whether it took ownership from the unique pointer, but since the standard actually guaran­tees that moving-from a unique pointer leaves it as null, we can exploit that to transmit that information in the unique pointer itself.

What is std::move(), and when should it be used?

Wikipedia Page on C++11 R-value references and move constructors

  1. In C++11, in addition to copy constructors, objects can have move constructors.

    (And in addition to copy assignment operators, they have move assignment operators.)
  2. The move constructor is used instead of the copy constructor, if the object has type "rvalue-reference" (Type &&).
  3. std::move() is a cast that produces an rvalue-reference to an object, to enable moving from it.

It's a new C++ way to avoid copies. For example, using a move constructor, a std::vector could just copy its internal pointer to data to the new object, leaving the moved object in an moved from state, therefore not copying all the data. This would be C++-valid.

Try googling for move semantics, rvalue, perfect forwarding.

Is the last appearance of a local variable treated as an rvalue in C++11?

In your example newitem is an lvalue (its a named object) so no move construtor will be used. Compiler will use move constructor if you provide an rvalue, as in:

V.push_back(std::move(newitem))

where xvalue is passed to push_back. So yes, you should use std::move explicitly.

Is it still necessary to use std move even if auto && has been used

If you indeed want to move all elements from myMap into anotherMap then yes you must call std::move(). The reason is that element here is still an lvalue. Its type is rvalue reference as declared, but the expression itself is still an lvalue, and thus the overload resolution will give back the lvalue reference constructor better known as the copy constructor.

This is a very common point of confusion. See for example this question.

Always keep in mind that std::move doesn't actually do anything itself, it just guarantees that the overload resolver will see an appropriately-typed rvalue instead of an lvalue associated with a given identifier.

Does C++11 guarantee a dying object will be moved rather than copied as an argument?

If f(coll) called f(vector<int>&&) instead of f(const vector<int>&) that would be a violation of the standard, because it would pick the wrong function overload.

It would also be rather confusing if the resolution of the call was different depending on where the call is located and whether there are any subsequent statements after the call using coll.

The special treatment is given to return values only:

If expression is an lvalue expression and the conditions for copy elision are met, or would be met, except that expression names a function parameter, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if expression were an rvalue expression (thus it may select the move constructor or a copy constructor taking reference to const), and if no suitable conversion is available, overload resolution is performed the second time, with lvalue expression (so it may select the copy constructor taking a reference to non-const).

Why do you use std::move when you have && in C++11?

First, there's probably a misconception in the question I'll address:

Whenever you see T&& t in code (And T is an actual type, not a template type), keep in mind the value category of t is an lvalue(reference), not an rvalue(temporary) anymore. It's very confusing. The T&& merely means that t is constructed from an object that was an rvalue 1, but t itself is an lvalue, not an rvalue. If it has a name (in this case, t) then it's an lvalue and won't automatically move, but if it has no name (the result of 3+4) then it is an rvalue and will automatically move into it's result if it can. The type (in this case T&&) has almost nothing to do with the value category of the variable (in this case, an lvalue).

That being said, if you have T&& t written in your code, that means you have a reference to a variable that was a temporary, and it is ok to destroy if you want to. If you need to access the variable multiple times, you do not want to std::move from it, or else it would lose it's value. But the last time you acccess t it is safe to std::move it's value to another T if you wish. (And 95% of the time, that's what you want to do). All of this also applies to auto&& variables.

1. if T is a template type, T&& is a forwarding reference instead, in which case you use std::forward<T>(t) instead of std::move(t) the last time. See this question.

std::move on a C++ class does not move all members?

You create a variable foo

Foo foo{"Hello",2.0f};

Then declare a vector

std::vector<Foo> v;

Then call push_back which invokes a copy of your Foo

v.push_back(foo);
std::cout << foo << std::endl;

Then you std::move(foo), which invalidates your foo instance

v.push_back(std::move(foo));

Trying to cout foo is now undefined behavior, as the internals are may be garbage.

std::cout << foo << std::endl;

While you technically could std::move a primitive, most implementations will simply invoke a copy because it is cheaper and faster.

Is it allowed to self-move an object in C++?

From cpp ref:

Also, the standard library functions called with xvalue arguments may assume the argument is the only reference to the object; if it was constructed from an lvalue with std::move, no aliasing checks are made. However, self-move-assignment of standard library types is guaranteed to place the object in a valid (but usually unspecified) state:

std::vector<int> v = {2, 3, 3};
v = std::move(v); // the value of v is unspecified

Are there any pitfalls in using move() instead of std::move()?

ADL will only find things in std if the argument types are also in std.

If there is another symbol called move in another namespace that is being used, then the compiler won't know which you meant. There may not be one now, but future changes may bring one in.

Additionally, always having the std:: makes it a lot easier for people reading your code later. It makes it clear that it's not a move that you've defined yourself or included from another library. Especially useful if they've not heard of the (relatively new) std::move before.



Related Topics



Leave a reply



Submit