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.
Related
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.
I've got this code:
SomeType::SomeType(std::vector<Item>&& container, const float someOtherPArameter)
: internal_container(std::move(container))
{
// some code here
}
Can somebody explain to me why a move constructor does not call for the 'internal_container' without 'std::move'?
The move constructor is called whenever an object is initialized from xvalue of the same type. You can create that xvalue by calling std::move(x). Declaring a parameter as an rvalue reference will not automatically make it an xvalue.
Because
SomeType::SomeType(std::vector<Item>&& container, const float someOtherPArameter)
: internal_container(container)
{
// the parameter container is in scope here
}
It would be pretty surprising if, inside the constructor body, accesses to the parameter container found a moved-from object. (It would also break code that was perfectly valid in C++03)
That's why you have to explicitly enable move.
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.
Let's say I got a Foo class containing an std::vector constructed from std::unique_ptr objects of another class, Bar.
typedef std::unique_ptr<Bar> UniqueBar;
class Foo {
std::vector<UniqueBar> bars;
public:
void AddBar(UniqueBar&& bar);
};
void Foo::AddBar(UniqueBar&& bar) {
bars.push_back(bar);
}
This one results in a compilation error (in g++ 4.8.1) saying that the the copy constructor of std::unique_ptr is deleted, which is reasonable. The question here is, since the bar argument is already an rvalue reference, why does the copy constructor of std::unique_ptr is called instead of its move constructor?
If I explicitly call std::move in Foo::AddBar then the compilation issue goes away but I don't get why this is needed. I think it's quite redundant.
So, what am I missing?
Basically, every object which has a name is an lvalue. When you pass an object to a function using an rvalue reference the function actually sees an lvalue: it is named. What the rvalue reference does, however, indicate is that it came from an object which is ready to be transferred.
Put differently, rvalue references are assymmetrical:
they can only receive rvalues, i.e., either temporary objects, objects about to go away, or objects which look as if they are rvalues (e.g., the result of std::move(o))
the rvalue reference itself looks, however, like an lvalue
Confusing as it might seem, an rvalue-reference binds to an rvalue, but used as an expression is an lvalue.
bar is actually an lvalue, so you need to pass it through std::move, so that it is seen as an rvalue in the call to push_back.
The Foo::AddBar(UniqueBar&& bar) overload simply ensures that this overload is picked when an rvalue is passed in a call to Foo::AddBar. But the bar argument itself has a name and is an lvalue.
bar is defined as an rvalue-reference, but its value-category is an lvalue. This is so because the object has a name. If it has a name, it's an lvalue. Therefore an explicit std::move is necessary because the intention is to get rid of the name and return an xvalue (eXpiring-rvalue).
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.