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.
Related
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?
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.
unique_ptr<T> does not allow copy construction, instead it supports move semantics. Yet, I can return a unique_ptr<T> from a function and assign the returned value to a variable.
#include <iostream>
#include <memory>
using namespace std;
unique_ptr<int> foo()
{
unique_ptr<int> p( new int(10) );
return p; // 1
//return move( p ); // 2
}
int main()
{
unique_ptr<int> p = foo();
cout << *p << endl;
return 0;
}
The code above compiles and works as intended. So how is it that line 1 doesn't invoke the copy constructor and result in compiler errors? If I had to use line 2 instead it'd make sense (using line 2 works as well, but we're not required to do so).
I know C++0x allows this exception to unique_ptr since the return value is a temporary object that will be destroyed as soon as the function exits, thus guaranteeing the uniqueness of the returned pointer. I'm curious about how this is implemented, is it special cased in the compiler or is there some other clause in the language specification that this exploits?
is there some other clause in the language specification that this exploits?
Yes, see 12.8 §34 and §35:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object [...]
This elision of copy/move operations, called copy elision, is permitted [...]
in a return statement in a function with a class return type, when the expression is the name of
a non-volatile automatic object with the same cv-unqualified type as the function return type [...]
When the criteria for elision of a copy operation are met 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.
Just wanted to add one more point that returning by value should be the default choice here because a named value in the return statement in the worst case, i.e. without elisions in C++11, C++14 and C++17 is treated as an rvalue. So for example the following function compiles with the -fno-elide-constructors flag
std::unique_ptr<int> get_unique() {
auto ptr = std::unique_ptr<int>{new int{2}}; // <- 1
return ptr; // <- 2, moved into the to be returned unique_ptr
}
...
auto int_uptr = get_unique(); // <- 3
With the flag set on compilation there are two moves (1 and 2) happening in this function and then one move later on (3).
This is in no way specific to std::unique_ptr, but applies to any class that is movable. It's guaranteed by the language rules since you are returning by value. The compiler tries to elide copies, invokes a move constructor if it can't remove copies, calls a copy constructor if it can't move, and fails to compile if it can't copy.
If you had a function that accepts std::unique_ptr as an argument you wouldn't be able to pass p to it. You would have to explicitly invoke move constructor, but in this case you shouldn't use variable p after the call to bar().
void bar(std::unique_ptr<int> p)
{
// ...
}
int main()
{
unique_ptr<int> p = foo();
bar(p); // error, can't implicitly invoke move constructor on lvalue
bar(std::move(p)); // OK but don't use p afterwards
return 0;
}
unique_ptr doesn't have the traditional copy constructor. Instead it has a "move constructor" that uses rvalue references:
unique_ptr::unique_ptr(unique_ptr && src);
An rvalue reference (the double ampersand) will only bind to an rvalue. That's why you get an error when you try to pass an lvalue unique_ptr to a function. On the other hand, a value that is returned from a function is treated as an rvalue, so the move constructor is called automatically.
By the way, this will work correctly:
bar(unique_ptr<int>(new int(44));
The temporary unique_ptr here is an rvalue.
I think it's perfectly explained in item 25 of Scott Meyers' Effective Modern C++. Here's an excerpt:
The part of the Standard blessing the RVO goes on to say that if the conditions for the RVO are met, but compilers choose not to perform copy elision, the object being returned must be treated as an rvalue. In effect, the Standard requires that when the RVO is permitted, either copy elision takes place or std::move is implicitly applied to local objects being returned.
Here, RVO refers to return value optimization, and if the conditions for the RVO are met means returning the local object declared inside the function that you would expect to do the RVO, which is also nicely explained in item 25 of his book by referring to the standard (here the local object includes the temporary objects created by the return statement). The biggest take away from the excerpt is either copy elision takes place or std::move is implicitly applied to local objects being returned. Scott mentions in item 25 that std::move is implicitly applied when the compiler choose not to elide the copy and the programmer should not explicitly do so.
In your case, the code is clearly a candidate for RVO as it returns the local object p and the type of p is the same as the return type, which results in copy elision. And if the compiler chooses not to elide the copy, for whatever reason, std::move would've kicked in to line 1.
One thing that i didn't see in other answers is To clarify another answers that there is a difference between returning std::unique_ptr that has been created within a function, and one that has been given to that function.
The example could be like this:
class Test
{int i;};
std::unique_ptr<Test> foo1()
{
std::unique_ptr<Test> res(new Test);
return res;
}
std::unique_ptr<Test> foo2(std::unique_ptr<Test>&& t)
{
// return t; // this will produce an error!
return std::move(t);
}
//...
auto test1=foo1();
auto test2=foo2(std::unique_ptr<Test>(new Test));
I would like to mention one case where you must use std::move() otherwise it will give an error.
Case: If the return type of the function differs from the type of the local variable.
class Base { ... };
class Derived : public Base { ... };
...
std::unique_ptr<Base> Foo() {
std::unique_ptr<Derived> derived(new Derived());
return std::move(derived); //std::move() must
}
Reference: https://www.chromium.org/developers/smart-pointer-guidelines
I know it's an old question, but I think an important and clear reference is missing here.
From https://en.cppreference.com/w/cpp/language/copy_elision :
(Since C++11) In a return statement or a throw-expression, if the compiler cannot perform copy elision but the conditions for copy elision are met or would be met, except that the source is a function parameter, the compiler will attempt to use the move constructor even if the object is designated by an lvalue; see return statement for details.
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.
unique_ptr<T> does not allow copy construction, instead it supports move semantics. Yet, I can return a unique_ptr<T> from a function and assign the returned value to a variable.
#include <iostream>
#include <memory>
using namespace std;
unique_ptr<int> foo()
{
unique_ptr<int> p( new int(10) );
return p; // 1
//return move( p ); // 2
}
int main()
{
unique_ptr<int> p = foo();
cout << *p << endl;
return 0;
}
The code above compiles and works as intended. So how is it that line 1 doesn't invoke the copy constructor and result in compiler errors? If I had to use line 2 instead it'd make sense (using line 2 works as well, but we're not required to do so).
I know C++0x allows this exception to unique_ptr since the return value is a temporary object that will be destroyed as soon as the function exits, thus guaranteeing the uniqueness of the returned pointer. I'm curious about how this is implemented, is it special cased in the compiler or is there some other clause in the language specification that this exploits?
is there some other clause in the language specification that this exploits?
Yes, see 12.8 §34 and §35:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object [...]
This elision of copy/move operations, called copy elision, is permitted [...]
in a return statement in a function with a class return type, when the expression is the name of
a non-volatile automatic object with the same cv-unqualified type as the function return type [...]
When the criteria for elision of a copy operation are met 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.
Just wanted to add one more point that returning by value should be the default choice here because a named value in the return statement in the worst case, i.e. without elisions in C++11, C++14 and C++17 is treated as an rvalue. So for example the following function compiles with the -fno-elide-constructors flag
std::unique_ptr<int> get_unique() {
auto ptr = std::unique_ptr<int>{new int{2}}; // <- 1
return ptr; // <- 2, moved into the to be returned unique_ptr
}
...
auto int_uptr = get_unique(); // <- 3
With the flag set on compilation there are two moves (1 and 2) happening in this function and then one move later on (3).
This is in no way specific to std::unique_ptr, but applies to any class that is movable. It's guaranteed by the language rules since you are returning by value. The compiler tries to elide copies, invokes a move constructor if it can't remove copies, calls a copy constructor if it can't move, and fails to compile if it can't copy.
If you had a function that accepts std::unique_ptr as an argument you wouldn't be able to pass p to it. You would have to explicitly invoke move constructor, but in this case you shouldn't use variable p after the call to bar().
void bar(std::unique_ptr<int> p)
{
// ...
}
int main()
{
unique_ptr<int> p = foo();
bar(p); // error, can't implicitly invoke move constructor on lvalue
bar(std::move(p)); // OK but don't use p afterwards
return 0;
}
unique_ptr doesn't have the traditional copy constructor. Instead it has a "move constructor" that uses rvalue references:
unique_ptr::unique_ptr(unique_ptr && src);
An rvalue reference (the double ampersand) will only bind to an rvalue. That's why you get an error when you try to pass an lvalue unique_ptr to a function. On the other hand, a value that is returned from a function is treated as an rvalue, so the move constructor is called automatically.
By the way, this will work correctly:
bar(unique_ptr<int>(new int(44));
The temporary unique_ptr here is an rvalue.
I think it's perfectly explained in item 25 of Scott Meyers' Effective Modern C++. Here's an excerpt:
The part of the Standard blessing the RVO goes on to say that if the conditions for the RVO are met, but compilers choose not to perform copy elision, the object being returned must be treated as an rvalue. In effect, the Standard requires that when the RVO is permitted, either copy elision takes place or std::move is implicitly applied to local objects being returned.
Here, RVO refers to return value optimization, and if the conditions for the RVO are met means returning the local object declared inside the function that you would expect to do the RVO, which is also nicely explained in item 25 of his book by referring to the standard (here the local object includes the temporary objects created by the return statement). The biggest take away from the excerpt is either copy elision takes place or std::move is implicitly applied to local objects being returned. Scott mentions in item 25 that std::move is implicitly applied when the compiler choose not to elide the copy and the programmer should not explicitly do so.
In your case, the code is clearly a candidate for RVO as it returns the local object p and the type of p is the same as the return type, which results in copy elision. And if the compiler chooses not to elide the copy, for whatever reason, std::move would've kicked in to line 1.
One thing that i didn't see in other answers is To clarify another answers that there is a difference between returning std::unique_ptr that has been created within a function, and one that has been given to that function.
The example could be like this:
class Test
{int i;};
std::unique_ptr<Test> foo1()
{
std::unique_ptr<Test> res(new Test);
return res;
}
std::unique_ptr<Test> foo2(std::unique_ptr<Test>&& t)
{
// return t; // this will produce an error!
return std::move(t);
}
//...
auto test1=foo1();
auto test2=foo2(std::unique_ptr<Test>(new Test));
I would like to mention one case where you must use std::move() otherwise it will give an error.
Case: If the return type of the function differs from the type of the local variable.
class Base { ... };
class Derived : public Base { ... };
...
std::unique_ptr<Base> Foo() {
std::unique_ptr<Derived> derived(new Derived());
return std::move(derived); //std::move() must
}
Reference: https://www.chromium.org/developers/smart-pointer-guidelines
I know it's an old question, but I think an important and clear reference is missing here.
From https://en.cppreference.com/w/cpp/language/copy_elision :
(Since C++11) In a return statement or a throw-expression, if the compiler cannot perform copy elision but the conditions for copy elision are met or would be met, except that the source is a function parameter, the compiler will attempt to use the move constructor even if the object is designated by an lvalue; see return statement for details.