Confusion between rvalue references and const lvalue references as parameter - c++

So the parameter list for a copy constructor consists of an const lvalue reference, like const B& x.
The parameter list for a move constructor, however, consists of an rvalue reference, like B&& x.
When I discovered this, it seemed odd to me, so I tried to define a function taking a const lvalue reference as an argument:
void calculator(const Intvec& veccor)
{
cout << "in veccor" << veccor.m_size << "\n";
}
I already knew that const lvalue reference can bind to anything, so the result was sort of expected. I tried calling the function with both and lvalue and an rvalue and as expected, it all worked:
calculator(Intvec(33)); //works
Intvec newvec(22);
calculator(newvec); //works
I then tried to change the parameter list of calculator to an rvalue reference, and as expected, only the rvalue worked when calling it.
So since a const lvalue reference can take both lvalues and rvalues as an argument, why does the move constructor not just use a const lvalue reference instead of an rvalue reference?
And why not just always use const lvalue references in general, like in cases where you would not do the same as what is happening in a move constructor?

If a move constructor accepted a const lvalue reference, then such declaration of a move constructor would be indistinguishable from a copy constructor. There has to be a way to distinguish them, and the language has been specified such that a move constructor takes an rvalue reference as the argument.
A move constructor that would accept a const lvalue reference would not allow the moved from object to be modified, so you couldn't do anything that you couldn't do in a copy constructor. In fact, such move constructor would be in every way identical to a copy constructor. Why would one call something a move constructor when it is exactly the same as a copy constructor?
PS. Your experiment reveals an interesting fact: As long as a class has a copy constructor (and the move constructor is not explicitly deleted), whether it has a move constructor or not does not affect how that object can be used. In any case where a move is appropriate, a copy can be used instead if the class has no move constructor.

Related

Passing rvalue reference to const lvalue reference paremeter

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.

Understanding move semantics

I'm trying to get my head around move semantics. In particular I want to know how to create a 'move only' type. Here's my attempt:
class Movable {
Movable(const Movable&) = delete;
Movable &operator=(Movable &) = delete;
public:
Movable() { }
Movable(Movable&& rhs) { cout << "mov constructor called" << endl; }
Movable &operator=(Movable &&rhs) { cout << "Mov assignment called" << endl; return *this; }
};
int main() {
// (1) This works correctly
Movable mov_constructored = ([]{
return Movable();
})();
// (2) Why do I have to explicity use std::move?
Movable mov_assigned = std::move(mov_constructored);
// (3) The last line fails because it's trying to use the copy constructor.
// Is it possible to pass by mov?
mov_assigned = std::move(([](Movable mov_passed){
return mov_passed;
})(mov_constructored));
}
My main questions are (1) why is it that #2 requires me to explicitly express that I want to move rather than copy? Seems like a default behavior would be if there is no copy constructor, use the move constructor instead (assuming it exists). The actual behavior seems to just fail unless you explicitly declare move semantics.
Question (2) is essentially what is the proper way to pass a move only object? Is the only correct way to pass by reference (because move semantics only relate to assignment / return values perhaps?) or is there an actual way to 'move' an object into a function?
1) Because move_constructed is an lvalue, so it tries to call the lvalue assigment. We use std::move() to cast an lvalue reference to an rvalue reference (Note that std::move() doesn't move, its only a cast).
2) Again, the problem is that move_constructed is an lvalue, so the by-value argument is initialized using its copy ctor. If you pass an rvalue (Or cast an lvalue to an rvalue using std::move()), that call should work, because the by-value parameter is initialized using the move ctor. (See note)
NOTE: Since C++11, I prefer to talk about lvalue constructor and rvalue constructor, instead of classic copy constructor and C++11 move constructor. I think this wording makes simple to understand lvalue vs rvalue/move semantics:
The classic copy ctor is just a constructor which takes an lvalue to initialize an object. Since its an lvalue, we cannot stole its resources, because the lvalue possible will be used later. So we have to copy it.
The move ctor is just a constructor which takes an rvalue to initialize an object. Since its an rvalue, we can safely stole its data/resources, because the rvalue will be going out of scope later.
In C++11, it works the other way around -- it falls back to copying if it can't move. Copying is considered to be the safer operation, while moving is considered to be the faster one. So, where applicable, a move will be attempted to get performance, but will fall back to a copy. If you have a statement like:
Movable mov_assigned = mov_constructored;
Then this is explicitly requesting a copy, and a move is not considered a safe alternative to a copy. A C++ programmer would not be expecting mov_constructored to change after that statement.

std::move vs. compiler optimization

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.

Classes, Rvalues and Rvalue References

An lvalue is a value bound to a definitive region of memory whereas an rvalue is an expression value whose existence is temporary and who does not necessarily refer to a definitive region of memory. Whenever an lvalue is used in a position in which an rvalue is expected, the compiler performs an lvalue-to-rvalue conversion and then proceeds with evaluation.
http://www.eetimes.com/discussion/programming-pointers/4023341/Lvalues-and-Rvalues
Whenever we construct a temporary (anonymous) class object or return a temporary class object from a function, although the object is temporary, it is addressable. However, the object still is a valid rvalue. This means that the object is a) an addressable rvalue or b) is being implicitly converted from an lvalue to an rvalue when the compiler expects an lvalue to be used.
For instance:
class A
{
public:
int x;
A(int a) { x = a; std::cout << "int conversion ctor\n"; }
A(A&) { std::cout << "lvalue copy ctor\n"; }
A(A&&) { std::cout << "rvalue copy ctor\n"; }
};
A ret_a(A a)
{
return a;
}
int main(void)
{
&A(5); // A(5) is an addressable object
A&& rvalue = A(5); // A(5) is also an rvalue
}
We also know that temporary objects returned (in the following case a) by functions are lvalues as this code segment:
int main(void)
{
ret_a(A(5));
}
yields the following output:
int conversion ctor
lvalue copy ctor
Indicating that the call to the function ret_a using actual argument A(5) calls the conversion constructor A::A(int) which constructs the function's formal argument a with the value 5.
When the function completes execution, it then constructs a temporary A object using a as its argument, which invokes A::A(A&). However, if we were to remove A::A(A&) from the list of overloaded constructors, the returned temporary object would still match the rvalue-reference constructor A::A(A&&).
This is what I'm not quite understanding: how can the object a match both an rvalue reference and an lvalue reference? It is clear that A::A(A&) is a better match than A::A(A&&) (and therefore a must be an lvalue). But, because an rvalue reference cannot be initialized to an lvalue, given that the formal argument a is an lvalue, it should not be able to match the call to A::A(A&&). If the compiler is making an lvalue-to-rvalue conversion it would be trivial. The fact that a conversion from 'A' to 'A&' is also trivial, both functions should have identical implicit conversion sequence ranks and therefore, the compiler should not be able to deduce the best-matching function when both A::A(A&) and A::A(A&&) are in the overloaded function candidate set.
Moreover, the question (which I previously asked) is:
How can a given object match both an rvalue reference and an lvalue reference?
For me:
int main(void)
{
ret_a(A(5));
}
Yields:
int conversion ctor
rvalue copy ctor
(i.e. rvalue, not lvalue). This is a bug in your compiler. However it is understandable as the rules for this behavior changed only a few months ago (Nov. 2010). More on this below.
When the function completes execution,
it then constructs a temporary A
object using a as its argument, which
invokes A::A(A&).
Actually no. When the function ret_a completes execution, it then constructs a temporary A object using a as its argument, which invokes A:A(A&&). This is due to [class.copy]/p33]1:
When the criteria for elision of a
copy operation are met or would be met
save for the fact that the source
object is a function parameter, and
the object to be copied is designated
by an lvalue, overload resolution to
select the constructor for the copy is
first performed as if the object were
designated by an rvalue. If overload
resolution fails, or if the type of
the first parameter of the selected
constructor is not an rvalue reference
to the object’s type (possibly
cv-qualified), overload resolution is
performed again, considering the
object as an lvalue. [ Note: This
two-stage overload resolution must be
performed regardless of whether copy
elision will occur. It determines the
constructor to be called if elision is
not performed, and the selected
constructor must be accessible even if
the call is elided. — end note ]
However if you remove the A::A(A&&) constructor, then A::A(&) will be chosen for the return. Although in this case, then the construction of the argument a will fail because you can't construct it using an rvalue. However ignoring that for the moment, I believe your ultimate question is:
How can a given object match both an
rvalue reference and an lvalue
reference?
in referring to the statement:
return a;
And the answer is in the above quoted paragraph from the draft standard: First overload resolution is tried as if a is an rvalue. And if that fails, overload resolution is tried again using a as an lvalue. This two-stage process is tried only in the context wherein copy elision is permissible (such as a return statement).
The C++0x draft has just recently been changed to allow the two-stage overload resolution process when returning arguments that have been passed by value (as in your example). And that is the reason for the varying behavior from different compilers that we are seeing.

Move constructor on derived object

When you have a derived object with a move constructor, and the base object also has move semantics, what is the proper way to call the base object move constructor from the derived object move constructor?
I tried the most obvious thing first:
Derived(Derived&& rval) : Base(rval)
{ }
However, this seems to end up calling the Base object's copy constructor. Then I tried explicitly using std::move here, like this:
Derived(Derived&& rval) : Base(std::move(rval))
{ }
This worked, but I'm confused why it's necessary. I thought std::move merely returns an rvalue reference. But since in this example rval is already an rvalue reference, the call to std::move should be superfluous. But if I don't use std::move here, it just calls the copy constructor. So why is the call to std::move necessary?
rval is not a Rvalue. It is an Lvalue inside the body of the move constructor. That's why we have to explicitly invoke std::move.
Refer this. The important note is
Note above that the argument x is
treated as an lvalue internal to the
move functions, even though it is
declared as an rvalue reference
parameter. That's why it is necessary
to say move(x) instead of just x when
passing down to the base class. This
is a key safety feature of move
semantics designed to prevent
accidently moving twice from some
named variable. All moves occur only
from rvalues, or with an explicit cast
to rvalue such as using std::move. If
you have a name for the variable, it
is an lvalue.
Named R-value references are treated as L-value.
So we need std::move to convert it to R-Value.
You really should use std::forward(obj) rather than std::move(obj). Forward will return the proper rvalue or lvalue based on the what obj is whereas move will turn an lvalue into an rvalue.