In C++ , What's So Special About "_Move_H"

in C++ , what's so special about _MOVE_H ?

just run grep _MOVE_H in /usr/include/c++ on your machine

for me :

c++/4.5.0/bits/move.h:#ifndef _MOVE_H

As a rule of thumb, don't use things (really anything) prefixed by _ or __. It's reserved for internal usage.
Use SOMETHING_MOVE_H (usually name of the company, ...).

I guess it's a new header used to add the move semantic to c++0x.

Why does C++11 have implicit moves for value parameters, but not for rvalue parameters?

The standardization committee expended great effort in creating wording so that moves would only ever happen in exactly two circumstances:

  1. When it is clearly safe to do so.
  2. When the user explicitly asks (via std::move or a similar cast).

A value parameter will unquestionably be destroyed at the end of the function. Therefore, returning it by move is clearly safe; it can't be touched by other code after the return (not unless you're deliberately trying to break things, in which case you probably triggered undefined behavior). Therefore, it can be moved from in the return.

A && variable could be referring to a temporary. But it could be referring to an lvalue (a named variable). It is therefore not clearly safe to move from it; the original variable could be lurking around. And since you didn't explicitly ask to move from it (ie: you didn't call std::move in this function), no movement can take place.

The only time a && variable will be implicitly moved from (ie: without std::move) is when you return it. std::move<T> returns a T&&. It is legal for that return value to invoke the move constructor, because it is a return value.

Now it is very difficult to call A func(A &&a) with an lvalue without calling std::move (or an equivalent cast). So technically, it should be fine for parameters of && type to be implicitly moved from. But the standards committee wanted moves to be explicit for && types, just to make sure that movement didn't implicitly happen within the scope of this function. That is, it can't use outside-of-function knowledge about where the && comes from.

In general, you should only take parameters by && in two cases: either you're writing a move constructor (or move assignment operator, but even that can be done by value), or you're writing a forwarding function. There may be a few other cases, but you shouldn't take && to a type unless you have something special in mind. If A is a moveable type, then just take it by value.

Advantages of pass-by-value and std::move over pass-by-reference

  1. Did I understand correctly what is happening here?

Yes.


  1. Is there any upside of using std::move over passing by reference and just calling m_name{name}?

An easy to grasp function signature without any additional overloads. The signature immediately reveals that the argument will be copied - this saves callers from wondering whether a const std::string& reference might be stored as a data member, possibly becoming a dangling reference later on. And there is no need to overload on std::string&& name and const std::string& arguments to avoid unnecessary copies when rvalues are passed to the function. Passing an lvalue

std::string nameString("Alex");
Creature c(nameString);

to the function that takes its argument by value causes one copy and one move construction. Passing an rvalue to the same function

std::string nameString("Alex");
Creature c(std::move(nameString));

causes two move constructions. In contrast, when the function parameter is const std::string&, there will always be a copy, even when passing an rvalue argument. This is clearly an advantage as long as the argument type is cheap to move-construct (this is the case for std::string).

But there is a downside to consider: the reasoning doesn't work for functions that assign the function argument to another variable (instead of initializing it):

void setName(std::string name)
{
m_name = std::move(name);
}

will cause a deallocation of the resource that m_name refers to before it's reassigned. I recommend reading Item 41 in Effective Modern C++ and also this question.

What does T&& (double ampersand) mean in C++11?

It declares an rvalue reference (standards proposal doc).

Here's an introduction to rvalue references.

Here's a fantastic in-depth look at rvalue references by one of Microsoft's standard library developers.

CAUTION: the linked article on MSDN ("Rvalue References: C++0x Features in VC10, Part 2") is a very clear introduction to Rvalue references, but makes statements about Rvalue references that were once true in the draft C++11 standard, but are not true for the final one! Specifically, it says at various points that rvalue references can bind to lvalues, which was once true, but was changed.(e.g. int x; int &&rrx = x; no longer compiles in GCC) – drewbarbs Jul 13 '14 at 16:12

The biggest difference between a C++03 reference (now called an lvalue reference in C++11) is that it can bind to an rvalue like a temporary without having to be const. Thus, this syntax is now legal:

T&& r = T();

rvalue references primarily provide for the following:

Move semantics. A move constructor and move assignment operator can now be defined that takes an rvalue reference instead of the usual const-lvalue reference. A move functions like a copy, except it is not obliged to keep the source unchanged; in fact, it usually modifies the source such that it no longer owns the moved resources. This is great for eliminating extraneous copies, especially in standard library implementations.

For example, a copy constructor might look like this:

foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}

If this constructor were passed a temporary, the copy would be unnecessary because we know the temporary will just be destroyed; why not make use of the resources the temporary already allocated? In C++03, there's no way to prevent the copy as we cannot determine whether we were passed a temporary. In C++11, we can overload a move constructor:

foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}

Notice the big difference here: the move constructor actually modifies its argument. This would effectively "move" the temporary into the object being constructed, thereby eliminating the unnecessary copy.

The move constructor would be used for temporaries and for non-const lvalue references that are explicitly converted to rvalue references using the std::move function (it just performs the conversion). The following code both invoke the move constructor for f1 and f2:

foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"

Perfect forwarding. rvalue references allow us to properly forward arguments for templated functions. Take for example this factory function:

template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}

If we called factory<foo>(5), the argument will be deduced to be int&, which will not bind to a literal 5, even if foo's constructor takes an int. Well, we could instead use A1 const&, but what if foo takes the constructor argument by non-const reference? To make a truly generic factory function, we would have to overload factory on A1& and on A1 const&. That might be fine if factory takes 1 parameter type, but each additional parameter type would multiply the necessary overload set by 2. That's very quickly unmaintainable.

rvalue references fix this problem by allowing the standard library to define a std::forward function that can properly forward lvalue/rvalue references. For more information about how std::forward works, see this excellent answer.

This enables us to define the factory function like this:

template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}

Now the argument's rvalue/lvalue-ness is preserved when passed to T's constructor. That means that if factory is called with an rvalue, T's constructor is called with an rvalue. If factory is called with an lvalue, T's constructor is called with an lvalue. The improved factory function works because of one special rule:

When the function parameter type is of
the form T&& where T is a template
parameter, and the function argument
is an lvalue of type A, the type A& is
used for template argument deduction.

Thus, we can use factory like so:

auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1); // calls foo(foo const&)

Important rvalue reference properties:

  • For overload resolution, lvalues prefer binding to lvalue references and rvalues prefer binding to rvalue references. Hence why temporaries prefer invoking a move constructor / move assignment operator over a copy constructor / assignment operator.
  • rvalue references will implicitly bind to rvalues and to temporaries that are the result of an implicit conversion. i.e. float f = 0f; int&& i = f; is well formed because float is implicitly convertible to int; the reference would be to a temporary that is the result of the conversion.
  • Named rvalue references are lvalues. Unnamed rvalue references are rvalues. This is important to understand why the std::move call is necessary in: foo&& r = foo(); foo f = std::move(r);

What can I do with a moved-from object?

Moved-from objects exist in an unspecified, but valid, state. That suggests that whilst the object might not be capable of doing much anymore, all of its member functions should still exhibit defined behaviour — including operator= — and all its members in a defined state- and it still requires destruction. The Standard gives no specific definitions because it would be unique to each UDT, but you might be able to find specifications for Standard types. Some like containers are relatively obvious — they just move their contents around and an empty container is a well-defined valid state. Primitives don't modify the moved-from object.

Side note: I believe it's T c = std::move(a) so that if the move constructor (or copy constructor if no move is provided) is explicit the function will fail.

Are the days of passing const std::string & as a parameter over?

The reason Herb said what he said is because of cases like this.

Let's say I have function A which calls function B, which calls function C. And A passes a string through B and into C. A does not know or care about C; all A knows about is B. That is, C is an implementation detail of B.

Let's say that A is defined as follows:

void A()
{
B("value");
}

If B and C take the string by const&, then it looks something like this:

void B(const std::string &str)
{
C(str);
}

void C(const std::string &str)
{
//Do something with `str`. Does not store it.
}

All well and good. You're just passing pointers around, no copying, no moving, everyone's happy. C takes a const& because it doesn't store the string. It simply uses it.

Now, I want to make one simple change: C needs to store the string somewhere.

void C(const std::string &str)
{
//Do something with `str`.
m_str = str;
}

Hello, copy constructor and potential memory allocation (ignore the Short String Optimization (SSO)). C++11's move semantics are supposed to make it possible to remove needless copy-constructing, right? And A passes a temporary; there's no reason why C should have to copy the data. It should just abscond with what was given to it.

Except it can't. Because it takes a const&.

If I change C to take its parameter by value, that just causes B to do the copy into that parameter; I gain nothing.

So if I had just passed str by value through all of the functions, relying on std::move to shuffle the data around, we wouldn't have this problem. If someone wants to hold on to it, they can. If they don't, oh well.

Is it more expensive? Yes; moving into a value is more expensive than using references. Is it less expensive than the copy? Not for small strings with SSO. Is it worth doing?

It depends on your use case. How much do you hate memory allocations?

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.

What is move semantics?

I find it easiest to understand move semantics with example code. Let's start with a very simple string class which only holds a pointer to a heap-allocated block of memory:

#include <cstring>
#include <algorithm>

class string
{
char* data;

public:

string(const char* p)
{
size_t size = std::strlen(p) + 1;
data = new char[size];
std::memcpy(data, p, size);
}

Since we chose to manage the memory ourselves, we need to follow the rule of three. I am going to defer writing the assignment operator and only implement the destructor and the copy constructor for now:

    ~string()
{
delete[] data;
}

string(const string& that)
{
size_t size = std::strlen(that.data) + 1;
data = new char[size];
std::memcpy(data, that.data, size);
}

The copy constructor defines what it means to copy string objects. The parameter const string& that binds to all expressions of type string which allows you to make copies in the following examples:

string a(x);                                    // Line 1
string b(x + y); // Line 2
string c(some_function_returning_a_string()); // Line 3

Now comes the key insight into move semantics. Note that only in the first line where we copy x is this deep copy really necessary, because we might want to inspect x later and would be very surprised if x had changed somehow. Did you notice how I just said x three times (four times if you include this sentence) and meant the exact same object every time? We call expressions such as x "lvalues".

The arguments in lines 2 and 3 are not lvalues, but rvalues, because the underlying string objects have no names, so the client has no way to inspect them again at a later point in time.
rvalues denote temporary objects which are destroyed at the next semicolon (to be more precise: at the end of the full-expression that lexically contains the rvalue). This is important because during the initialization of b and c, we could do whatever we wanted with the source string, and the client couldn't tell a difference!

C++0x introduces a new mechanism called "rvalue reference" which, among other things,
allows us to detect rvalue arguments via function overloading. All we have to do is write a constructor with an rvalue reference parameter. Inside that constructor we can do anything we want with the source, as long as we leave it in some valid state:

    string(string&& that)   // string&& is an rvalue reference to a string
{
data = that.data;
that.data = nullptr;
}

What have we done here? Instead of deeply copying the heap data, we have just copied the pointer and then set the original pointer to null (to prevent 'delete[]' from source object's destructor from releasing our 'just stolen data'). In effect, we have "stolen" the data that originally belonged to the source string. Again, the key insight is that under no circumstance could the client detect that the source had been modified. Since we don't really do a copy here, we call this constructor a "move constructor". Its job is to move resources from one object to another instead of copying them.

Congratulations, you now understand the basics of move semantics! Let's continue by implementing the assignment operator. If you're unfamiliar with the copy and swap idiom, learn it and come back, because it's an awesome C++ idiom related to exception safety.

    string& operator=(string that)
{
std::swap(data, that.data);
return *this;
}
};

Huh, that's it? "Where's the rvalue reference?" you might ask. "We don't need it here!" is my answer :)

Note that we pass the parameter that by value, so that has to be initialized just like any other string object. Exactly how is that going to be initialized? In the olden days of C++98, the answer would have been "by the copy constructor". In C++0x, the compiler chooses between the copy constructor and the move constructor based on whether the argument to the assignment operator is an lvalue or an rvalue.

So if you say a = b, the copy constructor will initialize that (because the expression b is an lvalue), and the assignment operator swaps the contents with a freshly created, deep copy. That is the very definition of the copy and swap idiom -- make a copy, swap the contents with the copy, and then get rid of the copy by leaving the scope. Nothing new here.

But if you say a = x + y, the move constructor will initialize that (because the expression x + y is an rvalue), so there is no deep copy involved, only an efficient move.
that is still an independent object from the argument, but its construction was trivial,
since the heap data didn't have to be copied, just moved. It wasn't necessary to copy it because x + y is an rvalue, and again, it is okay to move from string objects denoted by rvalues.

To summarize, the copy constructor makes a deep copy, because the source must remain untouched.
The move constructor, on the other hand, can just copy the pointer and then set the pointer in the source to null. It is okay to "nullify" the source object in this manner, because the client has no way of inspecting the object again.

I hope this example got the main point across. There is a lot more to rvalue references and move semantics which I intentionally left out to keep it simple. If you want more details please see my supplementary answer.



Related Topics



Leave a reply



Submit