Passing lvalue reference as an rvalue reference argument does not compile. Compiler could create a temporary object with its copy constructor and pass it as an rvalue, but it does not. Still, it does call the constructor, if types do not match.
I'm interested, why does it works that way? Which design logic is here in the C++ standard, that forces the compiler to treat copy-constructor in a different way?
void do_smth(string && s)
{}
void f(const char * s)
{
do_smth(s); // passes a temporary string constructed from const char*
}
void g(const string & s)
{
do_smth(s); // does not compile
}
void h(const string & s)
{
do_smth(string(s)); // ok again
}
What should I do if I do not want to implement the second signature do_smth(const string &)? Should I use pass-by-value void do_smth(string s) instead?
What are other differences betweeen by-value void do_smth(string s) and rvalue-reference void do_smth(string && s), given the object string has move constructor?
This is the whole point of rvalue references. They bind to rvalues, and not to lvalues.
That is how you ensure that the overload you wanted is invoked, when you have both an lvalue version and an rvalue version. For example, a copy constructor vs a move constructor.
It's a feature.
Also, your lvalue is const, which does not match the signature of do_smth even if it did not take an rvalue reference.
If you want do_smth to be able to take either kind of expression, either make it take a const string&, or make it a template and have it take a forwarding reference T&& (which looks like an rvalue reference but kind of isn't).
If you want do_smth to store its own version of the string then have it take a value: you can still avoid a copy if needs be by using std::move at the callsite (or by passing an rvalue, which will trigger the string's own move constructor).
See how all options are available and elegant, due to how the rvalue reference binding rules are made? It all fits together.
Deciding what to allow do_smth to take entirely depends on what "smth" it is going to "do", and only you can say what that is.
in h you give a non const copy of the const string, so the const disappears contrarily to the g case
of course with void do_smth(string s){...} the copy is made at the call and both const and non const string can be given in argument
Related
This may be obvious but I think it is something difficult to me. Given this:
void test(std::string&&) { }
std::string x{"test"};
test(std::move(x)); // ok
This code calls test() with a rvalue reference as parameter so the program compiles as I expect.
Now look at this:
void other_test(const std::string&) { }
std::string x{"test"};
other_test(std::move(x)); // ok???
And here I'm tilted. Why does this version compile? The std::move returns a && type; why then I don't get an error in the second method where I use const&?
I know that
int&& s = 5;
const int& s = 5;
is valid because in both cases I provide something that has not an lvalue, it has no addresses. Are && and const& equivalent? If no, are there differences?
std::move doesn't actually move anything out of it's own. It's just a fancy name for a cast to a T&&. Calling test like this test(std::move(x)); only shows that a T&& is implicitly convertible to a const T&. The compiler sees that test only accepts const T& so it converts the T&& returned from std::move to a const T&, that's all there is to it.
In simple terms:
&& can bind to non-const rvalues (prvalues and xvalues)
const && can bind to rvalues (const and non-const)
& can bind to non-const lvalues
const & can bind to rvalues (prvalues and xvalues) and lvalues (const and non-const for each). A.k.a. to anything.
If you want a function to expressly allow const-Lvalue objects, but expressly disallow Rvalue objects, write the function signature like this:
void test(const std::string&) { }
void test(std::string&&) = delete;//Will now be considered when matching signatures
int main() {
std::string string = "test";
test(string);//OK
//test(std::move(string));//Compile Error!
//test("Test2");//Compile Error!
}
test(std::string&& a) {
something(a) //--> not moved because it has lvalue
Names of variables are lvalues. a is a name of a variable, therefore a is an lvalue expression, and therefore it will not be moved from.
It's unclear what you mean by "has". a is an expression. It is a name of a reference, and references refer to objects. Value categories pertain to expressions, not objects.
test(const std::string& a): a is const lvalue reference and like before I have lvalue and rvalue. And plus more, in this case if I called
std::move(a)
where a is a const& the move works!
If by "works" you mean that it invokes a move constructor or assignment, then no, it does not work because no move construction or assignment has happened.
When you call std::move(x), an rvalue reference to the underlying data, test, will be returned. You are allowed to pass rvalue references as const (and const only!) reference parameters because an rvalue reference is implicitly convertible to a const reference. They are arguably the same thing from the function's point of view (a read only parameter). If you removed the const-qualifier of your parameter, this code would not compile:
void other_test(std::string&) { }
std::string x{"test"};
other_test(std::move(x)); //not okay because
//the function can potentially modify the parameter.
See Bo Qian's youtube video on rvalue vs lvalue.
I think there's something I'm not quite understanding about rvalue references. Why does the following fail to compile (VS2012) with the error 'foo' : cannot convert parameter 1 from 'int' to 'int &&'?
void foo(int &&) {}
void bar(int &&x) { foo(x); };
I would have assumed that the type int && would be preserved when passed from bar into foo. Why does it get transformed into int once inside the function body?
I know the answer is to use std::forward:
void bar(int &&x) { foo(std::forward<int>(x)); }
so maybe I just don't have a clear grasp on why. (Also, why not std::move?)
I always remember lvalue as a value that has a name or can be addressed. Since x has a name, it is passed as an lvalue. The purpose of reference to rvalue is to allow the function to completely clobber value in any way it sees fit. If we pass x by reference as in your example, then we have no way of knowing if is safe to do this:
void foo(int &&) {}
void bar(int &&x) {
foo(x);
x.DoSomething(); // what could x be?
};
Doing foo(std::move(x)); is explicitly telling the compiler that you are done with x and no longer need it. Without that move, bad things could happen to existing code. The std::move is a safeguard.
std::forward is used for perfect forwarding in templates.
Why does it get transformed into int once inside the function body?
It doesn't; it's still a reference to an rvalue.
When a name appears in an expression, it's an lvalue - even if it happens to be a reference to an rvalue. It can be converted into an rvalue if the expression requires that (i.e. if its value is needed); but it can't be bound to an rvalue reference.
So as you say, in order to bind it to another rvalue reference, you have to explicitly convert it to an unnamed rvalue. std::forward and std::move are convenient ways to do that.
Also, why not std::move?
Why not indeed? That would make more sense than std::forward, which is intended for templates that don't know whether the argument is a reference.
It's the "no name rule". Inside bar, x has a name ... x. So it's now an lvalue. Passing something to a function as an rvalue reference doesn't make it an rvalue inside the function.
If you don't see why it must be this way, ask yourself -- what is x after foo returns? (Remember, foo is free to move x.)
rvalue and lvalue are categories of expressions.
rvalue reference and lvalue reference are categories of references.
Inside a declaration, T x&& = <initializer expression>, the variable x has type T&&, and it can be bound to an expression (the ) which is an rvalue expression. Thus, T&& has been named rvalue reference type, because it refers to an rvalue expression.
Inside a declaration, T x& = <initializer expression>, the variable x has type T&, and it can be bound to an expression (the ) which is an lvalue expression (++). Thus, T& has been named lvalue reference type, because it can refer to an lvalue expression.
It is important then, in C++, to make a difference between the naming of an entity, that appears inside a declaration, and when this name appears inside an expression.
When a name appears inside an expression as in foo(x), the name x alone is an expression, called an id-expression. By definition, and id-expression is always an lvalue expression and an lvalue expressions can not be bound to an rvalue reference.
When talking about rvalue references it's important to distinguish between two key unrelated steps in the lifetime of a reference - binding and value semantics.
Binding here refers to the exact way a value is matched to the parameter type when calling a function.
For example, if you have the function overloads:
void foo(int a) {}
void foo(int&& a) {}
Then when calling foo(x), the act of selecting the proper overload involves binding the value x to the parameter of foo.
rvalue references are only about binding semantics.
Inside the bodies of both foo functions the variable a acts as a regular lvalue. That is, if we rewrite the second function like this:
void foo(int&& a) {
foo(a);
}
then intuitively this should result in a stack overflow. But it doesn't - rvalue references are all about binding and never about value semantics. Since a is a regular lvalue inside the function body, then the first overload foo(int) will be called at that point and no stack overflow occurs. A stack overflow would only occur if we explicitly change the value type of a, e.g. by using std::move:
void foo(int&& a) {
foo(std::move(a));
}
At this point a stack overflow will occur because of the changed value semantics.
This is in my opinion the most confusing feature of rvalue references - that the type works differently during and after binding. It's an rvalue reference when binding but it acts like an lvalue reference after that. In all respects a variable of type rvalue reference acts like a variable of type lvalue reference after binding is done.
The only difference between an lvalue and an rvalue reference comes when binding - if there is both an lvalue and rvalue overload available, then temporary objects (or rather xvalues - eXpiring values) will be preferentially bound to rvalue references:
void goo(const int& x) {}
void goo(int&& x) {}
goo(5); // this will call goo(int&&) because 5 is an xvalue
That's the only difference. Technically there is nothing stopping you from using rvalue references like lvalue references, other than convention:
void doit(int&& x) {
x = 123;
}
int a;
doit(std::move(a));
std::cout << a; // totally valid, prints 123, but please don't do that
And the keyword here is "convention". Since rvalue references preferentially bind to temporary objects, then it's reasonable to assume that you can gut the temporary object, i.e. move away all of its data away from it, because after the call it's not accessible in any way and is going to be destroyed anyway:
std::vector<std::string> strings;
string.push_back(std::string("abc"));
In the above snippet the temporary object std::string("abc") cannot be used in any way after the statement in which it appears, because it's not bound to any variable. Therefore push_back is allowed to move away its contents instead of copying it and therefore save an extra allocation and deallocation.
That is, unless you use std::move:
std::vector<std::string> strings;
std::string mystr("abc");
string.push_back(std::move(mystr));
Now the object mystr is still accessible after the call to push_back, but push_back doesn't know this - it's still assuming that it's allowed to gut the object, because it's passed in as an rvalue reference. This is why the behavior of std::move() is one of convention and also why std::move() by itself doesn't actually do anything - in particular it doesn't do any movement. It just marks its argument as "ready to get gutted".
The final point is: rvalue references are only useful when used in tandem with lvalue references. There is no case where an rvalue argument is useful by itself (exaggerating here).
Say you have a function accepting a string:
void foo(std::string);
If the function is going to simply inspect the string and not make a copy of it, then use const&:
void foo(const std::string&);
This always avoids a copy when calling the function.
If the function is going to modify or store a copy of the string, then use pass-by-value:
void foo(std::string s);
In this case you'll receive a copy if the caller passes an lvalue and temporary objects will be constructed in-place, avoiding a copy. Then use std::move(s) if you want to store the value of s, e.g. in a member variable. Note that this will work efficiently even if the caller passes an rvalue reference, that is foo(std::move(mystring)); because std::string provides a move constructor.
Using an rvalue here is a poor choice:
void foo(std::string&&)
because it places the burden of preparing the object on the caller. In particular if the caller wants to pass a copy of a string to this function, they have to do that explicitly;
std::string s;
foo(s); // XXX: doesn't compile
foo(std::string(s)); // have to create copy manually
And if you want to pass a mutable reference to a variable, just use a regular lvalue reference:
void foo(std::string&);
Using rvalue references in this case is technically possible, but semantically improper and totally confusing.
The only, only place where an rvalue reference makes sense is in a move constructor or move assignment operator. In any other situation pass-by-value or lvalue references are usually the right choice and avoid a lot of confusion.
Note: do not confuse rvalue references with forwarding references that look exactly the same but work totally differently, as in:
template <class T>
void foo(T&& t) {
}
In the above example t looks like a rvalue reference parameter, but is actually a forwarding reference (because of the template type), which is an entirely different can of worms.
I am trying to understand C++11 rvalue references and how to use them for optimal performance in my code.
Let's say we have a class A that has a member pointer to a large amount of dynamically allocated data.
Furthermore, a method foo(const A& a) that does something with an object of class A.
I want to prevent the copy constructor of A from being called when an object of A is passed to the function foo, since in that case it will perform a deep copy of the underlying heap data.
I tested passing an lvalue reference:
A a;
foo(a);
and passing an rvalue reference:
foo(A());
In both cases the copy constructor was not called.
Is this expected or is this due to some optimization of my compiler (Apple LLVM 5.1)? Is there any specification about this?
That is expected. If you pass an argument to a reference type parameter (whether lvalue or rvalue reference), the object will not be copied. That is the whole point of references.
The confusion you're having is pretty common. The choice of copy or move constructor only occurs when passing an object by value. For example:
void foo(A a);
When passing an A object to this function, the compiler will determine whether to use the copy or move constructor depending on whether the expression you pass is an lvalue or rvalue expression.
On the other hand, none of the following functions would even try to invoke the copy or move constructor because no object is being constructed:
void foo(A& a);
void foo(const A& a);
void foo(A&& a);
void foo(const A&& a);
It's important to note that you should rarely (if ever) have any reason to write a function, other than a move constructor/assignment operator, that takes an rvalue reference. You should be deciding between passing by value and passing by const lvalue reference:
If you're going to need a copy of the object inside the function anyway (perhaps because you want to modify a copy or pass it to another function), take it by value (A). This way, if you're given an lvalue, it'll have to be copied (you can't avoid this), but if you're given an rvalue, it'll be optimally moved into your function.
If you're not going to need a copy of the object, take it by const lvalue reference (const A&). This way, regardless of whether you're given an lvalue or rvalue, no copy will take place. You shouldn't use this when you do need to copy it though, because it prevents you from utilising move semantics.
From the sounds of it, you're not going to make any copies at all, so a const A& parameter would work.
For example:
void f(T&& t); // probably making a copy of t
void g()
{
T t;
// do something with t
f(std::move(t));
// probably something else not using "t"
}
Is void f(T const& t) equivalent in this case because any good compiler will produce the same code? I'm interested in >= VC10 and >= GCC 4.6 if this matters.
EDIT:
Based on the answers, I'd like to elaborate the question a bit:
Comparing rvalue-reference and pass-by-value approaches, it's so easy to forgot to use std::move in pass-by-value. Can compiler still check that no more changes are made to the variable and eliminate an unnecessary copy?
rvalue-reference approach makes only optimized version "implicit", e.g. f(T()), and requires the user to explicitly specify other cases, like f(std::move(t)) or to explicitly make a copy f(T(t)); if the user isn't done with t instance. So, in this optimization-concerned light, is rvalue-reference approach considered good?
It's definitely not the same. For once T && can only bind to rvalues, while T const & can bind both to rvalues and to lvalues. Second, T const & does not permit any move optimizations. If you "probably want to make a copy of t", then T && allows you to actually make a move-copy of t, which is potentially more efficient.
Example:
void foo(std::string const & s) { std::string local(s); /* ... */ }
int main()
{
std::string a("hello");
foo(a);
}
In this code, the string buffer containing "hello" must exist twice, once in the body of main, and another time in the body of foo. By contrast, if you used rvalue references and std::move(a), the very same string buffer can be "moved around" and only needs to be allocated and populated one single time.
As #Alon points out, the right idiom is in fact passing-by-value:
void foo(std::string local) { /* same as above */ }
int main()
{
std::string a("hello");
foo(std::move(a));
}
Well, it depends what f does with t, if it creates a copy of it, then I would even go at length of doing this:
void f(T t) // probably making a copy of t
{
m_newT = std::move(t); // save it to a member or take the resources if it is a c'tor..
}
void g()
{
T t;
// do something with t
f(std::move(t));
// probably something else not using "t"
}
Then you allow the move c'tors optimization to happen, you take 't' resources in any case, and if it was 'moved' to your function, then you even gain the non copy of moving it to the function, and if it was not moved then you probably had to have one copy
Now if at later on in the code you'd have:
f(T());
Then ta da, free move optimization without the f user even knowing..
Note that quote: "is void f(T const& t) equivalent in this case because any good compiler will produce the same code?"
It is not equivelent, it is LESS work, because only the "pointer" is transferred and no c'tors are called at all, neither move nor anything else
Taking an const lvalue reference and taking an rvalue reference are two different things.
Similarities:
Neither will cause an copy or move to take place because they are both references. A reference just references an object, it doesn't copy/move it in any way.
Differences:
A const lvalue reference will bind to anything (lvalue or rvalue). An rvalue reference will only bind to non-const rvalues - much more limited.
The parameter inside the function cannot be modified when it is a const lvalue reference. It can be modified when it's an rvalue reference (since it is non-const).
Let's look at some examples:
Taking const lvalue reference: void f(const T& t);
Passing an lvalue:
T t; f(t);
Here, t is an lvalue expression because it's the name of the object. A const lvalue reference can bind to anything, so t will happily be passed by reference. Nothing is copied, nothing is moved.
Passing an rvalue:
f(T());
Here, T() is an rvalue expression because it creates a temporary object. Again, a const lvalue reference can bind to anything, so this is okay. Nothing is copied, nothing is moved.
In both of these cases, the t inside the function is a reference to the object passed in. It can't be modified by the reference is const.
Taking an rvalue reference: `void f(T&& t);
Passing an lvalue:
T t;
f(t);
This will give you a compiler error. An rvalue reference will not bind to an lvalue.
Passing an rvalue:
f(T());
This will be fine because an rvalue reference can bind to an rvalue. The reference t inside the function will refer to the temporary object created by T().
Now let's consider std::move. First things first: std::move doesn't actually move anything. The idea is that you give it an lvalue and it turns it into an rvalue. That's all it does. So now, if your f takes an rvalue reference, you could do:
T t;
f(std::move(t));
This works because, although t is an lvalue, std::move(t) is an rvalue. Now the rvalue reference can bind to it.
So why would you ever take an rvalue reference argument? In fact, you shouldn't need to do it very often, except for defining move constructors and assignment operators. Whenever you define a function that takes an rvalue reference, you almost certainly want to give a const lvalue reference overload. They should almost always come in pairs:
void f(const T&);
void f(T&&);
Why is this pair of functions useful? Well, the first will be called whenever you give it an lvalue (or a const rvalue) and the second will be called whenever you give it a modifiable rvalue. Receiving an rvalue usually means that you've been given a temporary object, which is great news because that means you can ravage its insides and perform optimizations based on the fact that you know it's not going to exist for much longer.
So having this pair of functions allows you to make an optimization when you know you're getting a temporary object.
There's a very common example of this pair of functions: the copy and move constructors. They are usually defined like so:
T::T(const T&); // Copy constructor
T::T(T&&); // Move constructor
So a move constructor is really just a copy constructor that is optimized for when receiving a temporary object.
Of course, the object being passed isn't always a temporary object. As we've shown above, you can use std::move to turn an lvalue into an rvalue. Then it appears to be a temporary object to the function. Using std::move basically says "I allow you to treat this object as a temporary object." Whether it actually gets moved from or not is irrelevant.
However, beyond writing copy constructors and move constructors, you'd better have a good reason for using this pair of functions. If you're writing a function that takes an object and will behave exactly the same with it regardless of whether its a temporary object or not, simply take that object by value! Consider:
void f(T t);
T t;
f(t);
f(T());
In the first call to f, we are passing an lvalue. That will be copied into the function. In the second call to f, we are passing an rvalue. That object will be moved into the function. See - we didn't even need to use rvalue references to cause the object to be moved efficiently. We just took it by value! Why? Because the constructor that is used to make the copy/move is chosen based on whether the expression is an lvalue or an rvalue. Just let the copy/move constructors do their job.
As to whether different argument types result in the same code - well that's a different question entirely. The compiler operates under the as-if rule. This simply means that as long as the program behaves as the standard dictates, the compiler can emit whatever code it likes. So the functions may emit the same code if they happen to do exactly the same thing. Or they may not. However, it's a bad sign if you're functions that take a const lvalue reference and an rvalue reference are doing the same thing.
In several places I've seen the recommended signatures of copy and move constructors given as:
struct T
{
T();
T(const T& other);
T(T&& other);
};
Where the copy constructor takes a const reference, and the move constructor takes a non-const rvalue reference.
As far as I can see though, this prevents me taking advantage of move semantics when returning const objects from a function, such as in the case below:
T generate_t()
{
const T t;
return t;
}
Testing this with VC11 Beta, T's copy constructor is called, and not the move constructor. Even using return std::move(t); the copy constructor is still called.
I can see how this makes sense, since t is const so shouldn't bind to T&&. Using const T&& in the move constructor signature works fine, and makes sense, but then you have the problem that because other is const, you can't null its members out if they need to be nulled out - it'll only work when all members are scalars or have move constructors with the right signature.
It looks like the only way to make sure the move constructor is called in the general case to have made t non-const in the first place, but I don't like doing that - consting things is good form and I wouldn't expect the client of T to know that they had to go against that form in order to increase performance.
So, I guess my question is twofold; first, should a move constructor take a const or non-const rvalue reference? And second: am I right in this line of reasoning? That I should stop returning things that are const?
It should be a non-const rvalue reference.
If an object is placed in read-only memory, you can't steal resources from it, even if its formal lifetime is ending shortly. Objects created as const in C++ are allowed to live in read-only memory (using const_cast to try to change them results in undefined behavior).
A move constructor should normally take a non-const reference.
If it were possible to move from a const object it would usually imply that it was as efficient to copy an object as it was to "move" from it. At this point there is normally no benefit to having a move constructor.
You are also correct that if you have a variable that you are potentially going to want to move from then it will need to be non-const.
As I understand it this is the reason that Scott Meyers has changed his advice on returning objects of class type by value from functions for C++11. Returning objects by const qualified value does prevent unintentionally modification of a temporary object but it also inhibits moving from the return value.
Should a move constructor take a const or non-const rvalue reference?
It should take non-const rvalue reference. The rvalue references first of all don't make sense in their const forms simply because you want to modify them (in a way, you want to "move" them, you want their internals for yourself ).
Also, they have been designed to be used without const and I believe the only use for a const rvalue reference is something very very arcane that Scott Meyers mentioned in this talk (from the time 42:20 to 44:47).
Am I right in this line of reasoning? That I should stop returning things that are const?
This is a bit of too general question to answer I reckon. In this context, I think it's worth mentioning that there's std::forward functionality that will preserve both rvalue-ness and lvalue-ness as well as const-ness and it will also avoid creating a temporary as a normal function would do should you return anything passed to it.
This returning would also cause the rvalue reference to be "mangled" into lvalue reference and you generally don't want that, hence, perfect forwarding with the aforementioned functionality solves the issue.
That being said, I suggest you simply take a look at the talk that I posted a link to.
In addition to what is said in other answers, sometimes there are reasons for a move constructor or a function to accept a const T&&. For example, if you pass the result of a function that returns a const object by value to a constructor, T(const T&) will be called instead of T(T&&) as one would probably expect (see function g below).
This is the reason behind deleting overloads that accept constT&& for std::ref and std::cref instead of those that accept T&&.
Specifically, the order of preference during overload resolution is as follows:
struct s {};
void f ( s&); // #1
void f (const s&); // #2
void f ( s&&); // #3
void f (const s&&); // #4
const s g ();
s x;
const s cx;
f (s ()); // rvalue #3, #4, #2
f (g ()); // const rvalue #4, #2
f (x); // lvalue #1, #2
f (cx); // const lvalue #2
See this article for more details.