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.
Related
I passed by the following code:
void enqueue(T&& value)
{
move_value_somewhere( T(std::move(value)));
}
void enqueue(const T& value)
{
enqueue(T(value));
}
The second function that copies the input parameter, calls the first function that moves it. Assuming the code is correct, how is that T(std::move(value)) doesn't move the value instead of copying it if it's called from the second function, it's probably from T() but I don't why.
In enqueue(T(value)); you first create a temporary copy of the passed value (T(value)). That temporary copy is then moved by your move constructor enqeue. So that code is similar to
T copy(value);
enqueue(std::move(copy));
In void enqueue(const T& value), a temporary T is being copy-constructed from value as input. That temporary is then passed to void enqueue(T&& value).
In void enqueue(T&& value), another temporary T is being move-constructed from value as input (assuming T has a move constructor, otherwise it will be copy-constructed instead). That temporary is then passed to move_value_somewhere(). That temporary T is actually unnecessary, since value is already an rvalue reference, so it could be moved to move_value_somewhere() directly, eg:
void enqueue(T&& value)
{
move_value_somewhere(std::move(value));
}
The move constructor is called inside the copy constructor
Actually, it is not, because the code shown is not for constructors at all, simply for normal non-static class methods. There is a big difference there. Constructors don't have return values, and can't call each other like this code is doing. But they can delegate to each other, but only from the member initialization list. See Constructors and member initializer lists.
enqueue() takes a T&&, which simply restricts it to that type, no constructor invocation occurs as a result of passing the argument because it is not a value-type.
When you call enqueue() however, you create a temporary T which will have its copy-constructor invoked from the const T& value that you pass into it.
Subsequently, the temporary becomes the parameter for enqueue() which in turn, then creates another prvalue T, this time initialised with your (now) lvalue parameter cast to T&& which, if T does has a move constructor, will invoke it.
Otherwise, if T does not have a move constructor (nor an explicitly deleted one), it will "fall-back" to invoking the copy-constructor instead, if present.
Since you are passing a temporary to move_value_somewhere() it would need to accept either T&&, const T& or T - under the provision that the type T has a viable constructor to make this all possible.
Let’s say I have the class MyClass with a correct move constructor and whose copy constructor is deleted. Now I am returning this class like this:
MyClass func()
{
return MyClass();
}
In this case the move constructor gets called when returning the class object and everything works as expected.
Now let’s say MyClass has an implementation of the << operator:
MyClass& operator<<(MyClass& target, const int& source)
{
target.add(source);
return target;
}
When I change the code above:
MyClass func()
{
return MyClass() << 5;
}
I get the compiler error, that the copy constructor cannot be accessed because it is deleted. But why is the copy constructor being used at all in this case?
Now I am returning this class via lvalue like this:
MyClass func()
{
return MyClass();
}
No, the returned expression is an xvalue (a kind of rvalue), used to initialise the result for return-by-value (things are a little more complicated since C++17, but this is still the gist of it; besides, you're on C++11).
In this case the move constructor gets called when returning the class object and everything works as expected.
Indeed; an rvalue will initialise an rvalue reference and thus the whole thing can match move constructors.
When I change the code above:
… now the expression is MyClass() << 5, which has type MyClass&. This is never an rvalue. It's an lvalue. It's an expression that refers to an existing object.
So, without an explicit std::move, that'll be used to copy-initialise the result. And, since your copy constructor is deleted, that can't work.
I'm surprised the example compiles at all, since a temporary can't be used to initialise an lvalue reference (your operator's first argument), though some toolchains (MSVS) are known to accept this as an extension.
then would return std::move(MyClass() << 5); work?
Yes, I believe so.
However that is very strange to look at, and makes the reader double-check to ensure there are no dangling references. This suggests there's a better way to accomplish this that results in clearer code:
MyClass func()
{
MyClass m;
m << 5;
return m;
}
Now you're still getting a move (because that's a special rule when returning local variables) without any strange antics. And, as a bonus, the << call is completely standard-compliant.
Your operator return by MyClass&. So you are returning an lvalue, not an rvalue that can be moved automatically.
You can avoid the copy by relying on the standard guarantees regarding NRVO.
MyClass func()
{
MyClass m;
m << 5;
return m;
}
This will either elide the object entirely, or move it. All on account of it being a function local object.
Another option, seeing as you are trying to call operator<< on an rvalue, is to supply an overload dealing in rvalue references.
MyClass&& operator<<(MyClass&& target, int i) {
target << i; // Reuse the operator you have, here target is an lvalue
return std::move(target);
}
That will make MyClass() << 5 itself well formed (see the other answer for why it isn't), and return an xvalue from which the return object may be constructed. Though such and overload for operator<< is not commonly seen.
Your operator<< takes its first parameter as a non-const reference. You can't bind a non-const reference to a temporary. But MyClass() returns the newly-created instance as a temporary.
Also, while func returns a value, operator<< returns a reference. So what else can it do but make a copy to return?
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.
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.