Given class:
class C
{
public:
C()
{
cout << "Dflt ctor.";
}
C(C& obj)
{
cout << "Copy ctor.";
}
C(C&& obj)
{
cout << "Move ctor.";
}
C& operator=(C& obj)
{
cout << "operator=";
return obj;
}
C& operator=(C&& obj)
{
cout << "Move operator=";
return obj;
}
};
and then in main:
int main(int argc, char* argv[])
{
C c;
C d = c;
C e;
e = c;
return 0;
}
as you will see from the output the "regular" version of copy ctor and operator= are invoked but not those with rvalue args. So I would like to ask in what circumstances will the move ctor and operator=(C&&) be invoked?
The move constructor will be invoked when the right-hand side is a temporary, or something that has been explicitly cast to C&& either using static_cast<C&&> or std::move.
C c;
C d(std::move(c)); // move constructor
C e(static_cast<C&&>(c)); // move constructor
C f;
f=std::move(c); // move assignment
f=static_cast<C&&>(c); // move assignment
C g((C())); // move construct from temporary (extra parens needed for parsing)
f=C(); // move assign from temporary
IIRC, you have to use C d = std::move(c) to use the move constructor.
An example not tested but that could explain better the use of move constructor :
C&& foo() { C c; return std::move(c); }
All of your variables are lvalues and thus cannot be moved implicitly, since you may need to access them later. In addition, copy constructors and assignment operators take const references.
Rvalue references work on, well, rvalues, that is, temporary objects. In order to see the move constructor used, first, you will have to actually create a temporary object. In addition, don't forget that RVO still applies and may well nail any or all of your std::cout calls.
You can create an rvalue from an lvalue using std::move(lvalue).
std::swap(c,e); // c and e variables declared in your _tmain()
would call the move constructor.
More realistic example of using move operator would be if you have a static class which returns C&& created on the local stack like this:
static C&& CreateC()
{
C c();
//do something with your object
return c;
}
and then you invoke it like this:
C x = CreateC(); // move operator is invoked here
Related
I have this code, taken from here by the way http://www.cplusplus.com/doc/tutorial/classes2/
// move constructor/assignment
#include <iostream>
#include <string>
#include <utility>
using namespace std;
class Example6
{
string* ptr;
public:
Example6(const string& str) :
ptr(new string(str))
{
cout << "DONT MOVE " << '\n';
}
~Example6()
{
delete ptr;
}
// move constructor
Example6(Example6&& x) :
ptr(x.ptr)
{
cout << "MOVE " << '\n';
x.ptr = nullptr;
}
// move assignment
Example6& operator=(Example6&& x)
{
delete ptr;
ptr = x.ptr;
x.ptr = nullptr;
return *this;
}
// access content:
const string& content() const
{
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs)
{
return Example6(content() + rhs.content());
}
};
int main()
{
Example6 foo("Exam");
Example6 bar = Example6("ple"); // move-construction
foo = foo + bar; // move-assignment
cout << "foo's content: " << foo.content() << '\n';
return 0;
}
I only added output in constructor to see which is being called. To my surprise it is always the first one, copy constructor. Why does it happen? I did some research and found some info about elision. Is it somehow possible to prevent it and always call move constructor?
Also, as a side note, as I said this code is from cplusplus.com. However, I read about move semantics in some other places and I wonder if this move constructor here is done right. Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move, because x is rvalue reference that has a name, so it is really lvalue and we need to use move to cast it to be rvalue. Do i miss something, or is it really tutorial's mistake?
Btw, adding move doesn't solve my first problem.
So anything with a name is an lvalue.
An rvalue reference with a name is an lvalue.
An rvalue reference will bind to rvalues, but it itself is an lvalue.
So x in ptr(x.ptr) is an rvalue reference, but it has a name, so it is an lvalue.
To treat it as an rvalue, you need to do ptr( std::move(x).ptr ).
Of course, this is mostly useless, as moving a ptr does nothing as ptr is a dumb raw pointer.
You should be following the rule of 0 here.
class Example6 {
std::unique_ptr<string> ptr;
public:
Example6 (string str) : ptr(std::make_unique<string>(std::move(str))) {cout << "DONT MOVE " << '\n';}
Example6():Example6("") {}
~Example6 () = default;
// move constructor
Example6 (Example6&& x) = default;
// move assignment
Example6& operator= (Example6&& x) = default;
// access content:
const string& content() const {
if (!ptr) *this=Example6{};
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs) {
return Example6(content()+rhs.content());
}
};
because business logic and lifetime management don't belong intermixed in the same class.
While we are at it:
// addition:
Example6& operator+=(const Example6& rhs) & {
if (!ptr) *this = Example6{};
*ptr += rhs.content();
return *this;
}
// addition:
friend Example6 operator+(Example6 lhs, const Example6& rhs) {
lhs += rhs;
return lhs;
}
Copy constructor is called ... - why?
The premise of your question is faulty: The copy constructor is not called. In fact, the class is not copyable.
The first constructor is a converting constructor from std::string. The converting constructor is called because Example6 objects are initialised with a string argument. Once in each of these expressions:
Example6 foo("Exam")
Example6("ple")
Example6(content() + rhs.content()
... instead of move constructor
There are a few copy-initialisations by move in the program. However, all of them can be elided by the compiler.
Is it somehow possible to prevent it and always call move constructor?
There are a few mistakes that can prevent copy elision. For example, if you wrote the addition operator like this:
return std::move(Example6(content()+rhs.content()));
The compiler would fail to elide the move and probably tell you about it if you're lucky:
warning: moving a temporary object prevents copy elision
Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
There's no need. Moving a pointer is exactly the same as copying a pointer. Same holds for all fundamental types.
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move
ptr is not a string. It is a pointer to a string. Copying a pointer does nothing to the pointed object.
PS. The example program is quite bad quality. There should never be owning bare pointers in C++.
I can say your class does not have a copy constructor.
Because copy ctor parameter have to be const and reference
class Example6{
public:
Example6(const Example6 &r);
};
Say I have a class that is move-only, and this class has methods that "chain". example:
struct C {
C() = default;
C(C const&) = delete;
C(C&&) = default;
C& chained() {
return *this;
}
int a;
};
C foo(C c) {
return c.chained();
}
int main()
{
auto o = foo(C{});
}
I get an error at the return statement of foo: "Use of deleted function 'C::C(const C&)'".
Why is trying to call the copy constructor? Shouldn't it be using the move constructor since its a return statement?
Why is trying to call the copy constructor? Shouldn't it be using the move constructor since its a return statement?
No. You are referring to, and misinterpreting, copy elision (as pertaining to NRVO). Only when the returned expression is a an id-expression (just a name) that refers to an object from the function's parameter list or a local, will a move be attempted first.
You don't return an id-expression, your expression is the result of a call to a member function.
After reading some materiales about rvalue reference i have more question then answers. From here i have read about rvalue ref:
Doc rvalue ref (1)
Doc rvalue ref (2)
Doc rvalue ref (3 - book)
Here i made a simple example to help me understand:
#include <iostream>
using namespace std;
class A
{
public:
A() :m_a(0), m_pa(nullptr) { cout << "constructor call" << endl; };
~A() { cout << "destructor call" << endl; };
A(A& other) :m_a(0), m_pa(nullptr)
{
cout << "copy constructor" << endl;
}
A(A&& other) :m_a(0), m_pa(nullptr)
{
cout << "move constructor" << endl;
}
A& operator=(A&& other)
{
this->m_a = other.m_a;
this->m_pa = other.m_pa;
other.m_a = 0;
other.m_pa = nullptr;
return *this;
}
A& operator=(A& other)
{
this->m_a = other.m_a;
this->m_pa = other.m_pa;
other.m_a = 0;
other.m_pa = nullptr;
return *this;
}
private:
int m_a;
int* m_pa;
};
int main()
{
A(test2);//constructor
A test4(test2);//copy constructor
//? - move constructor
return 0;
}
I don't understand what is so special with &&. In the above example i can do something like this with &.
A& operator=(A& other)
{
this->m_a = other.m_a; //copy value
this->m_pa = other.m_pa;//copy pointer address
other.m_a = 0;
other.m_pa = nullptr;//clean "other" object properties from preventing destructor to delete them and lose pointer address
return *this;
}
Question:
If i can do this with & without using extra memory allocation and copy operation why should i use &&?
How is a value value taken that has no identifier and saved?
Example 2:
#include <iostream>
using namespace std;
void printReference (int& value)
{
cout << "lvalue: value = " << value << endl;
}
void printReference (int&& value)
{
cout << "rvalue: value = " << value << endl;
}
int getValue ()
{
int temp_ii = 99;
return temp_ii;
}
int main()
{
int ii = 11;
printReference(ii);
printReference(getValue()); // printReference(99);
return 0;
}
Question:
Why to use && in this case and how does this help me? Why not just store the return of getValue and print it?
After you read some stuff about rvalues, here is some more material about rvalues.
I think the point you are probably missing, is not (only) what you can do but what you should do.
Your first example has several issues:
Your are not able to copy a const value to an instance of A.
const A a1;
A a2(a1); // won't compile
A a3;
a3 = a1; // won't compile
I don't understand what is so special with &&. In the above example i can do something like this with &.
Yes you could do what you suggested. But it is purely designed copy assigment. Consider this:
I wrote a shared library where my copy ctor is like you did in your suggestion. You don't have access to my code, just the header. In my copy ctor and assigment operator i take ownership of the instance you passed to my library. There is no description what the assignment is doing... Do you see the point, I must not take ownership of your instances! eg:
// my header:
// copy ctor
A& operator=(A& other);
// your code:
A a1;
A a2(a1); // from now on, a1 is broken and you don't know it!!!
cout << a1; // pseudo code: prints garbage, UD, crash!!!
You always should define copy-ctors/assignments parameters const:
A(A const& other);
A& operator=(A const& other);
// ...
const A a1;
A a2(a1); // will compile
A a3;
a3 = a1; // will compile + due to the fact a1 is passed const a3 cannot mutate a1 (does not take ownership)
cout << a1; // pseudo code: everything is fine
a3 = std::move(a1); // a3 takes ownership from a1 but you stated this explicitly as you want it
Here is a small example you can play with. Notice the copy constructor is const but the copy assignment is not. Then you can see how they differ.
If i can do this with & without using extra memory allocation and copy operation why should i use &&?
The assignment operators you wrote taking & lvalue references are very, very bad. You don't expect statements like
a = b;
to damage b, but that's what you're suggesting. The usual assignment operator takes a const& precisely because it shouldn't alter the right-hand-side of the expression.
So, you should use the rvalue-reference assignment operator (move assignment) when you do want to steal the right-hand-side's state, such as when it's an anonymous temporary or you explicitly move it:
a = return_anonymous_temporary(); // ok: the rhs value would expire anyway
a = std::move(b); // ok: the damage to b is explicit now
That behaviour shouldn't be the default, though.
If i can do this with & without using extra memory allocation and copy operation why should i use &&?
Because it allows you to overload a function for rvalues and lvalues and have different behaviour for each. You have same behaviour in both overloads, so you don't need an rvalue reference in this case.
More generally, an rvalue reference argument allows you to pass a temporary, while allowing a move from that argument.
Why to use && in this case and how does this help me?
It allowed you to print "rvalue" when the argument was an rvalue.
Why not just store the return of getValue and print it?
Then you won't be able to print "rvalue" for rvalues and "lvalue" for lvalues.
Except for move constructor/assignment operator, there are not many cases where r-values are useful.
I wrote the following c++ code trying to understand copy elision in c++.
#include <iostream>
using namespace std;
class B
{
public:
B(int x ) //default constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
};
int main()
{
B ob =5;
ob=6;
ob=7;
return 0;
}
This produces the following output:
Constructor called
Constructor called
Constructor called
I fail to understand why is the constructor being called thrice with each assignment to object ob.
B ob =5;
This uses the given constructor.
ob=6;
This uses the given constructor because there is not a B& operator=(int) function and 6 must be converted to type B. One path to do this is to temporarily construct a B and use it in the assignment.
ob=7;
Same answer as above.
I fail to understand why is the constructor being called thrice with each assignment
As I stated above you do not have a B& operator=(int) function but the compiler is happy to provide a copy assignment operator (i.e., B& operator=(const B&);) for you automatically. The compiler generated assignment operator is being called and it takes a B type and all int types can be converted to a B type (via the constructor you provided).
Note: You can disable the implicit conversion by using explicit (i.e., explicit B(int x);) and I would recommend the use of explicit except when implicit conversions are desired.
Example
#include <iostream>
class B
{
public:
B(int x) { std::cout << "B ctor\n"; }
B(const B& b) { std::cout << B copy ctor\n"; }
};
B createB()
{
B b = 5;
return b;
}
int main()
{
B b = createB();
return 0;
}
Example Output
Note: Compiled using Visual Studio 2013 (Release)
B ctor
This shows the copy constructor was elided (i.e., the B instance in the createB function is triggered but no other constructors).
Each time you assign an instance of the variable ob of type B an integer value, you are basically constructing a new instance of B thus calling the constructor. Think about it, how else would the compiler know how to create an instance of B if not through the constructor taking an int as parameter?
If you overloaded the assignment operator for your class B taking an int, it would be called:
B& operator=(int rhs)
{
cout << "Assignment operator" << endl;
}
This would result in the first line: B ob = 5; to use the constructor, while the two following would use the assignment operator, see for yourself:
Constructor called
Assignment operator
Assignment operator
http://ideone.com/fAjoA4
If you do not want your constructor taking an int to be called upon assignment, you can declare it explicit like this:
explicit B(int x)
{
cout << "Constructor called" << endl;
}
This would cause a compiler error with your code, since it would no longer be allowed to implicitly construct an instance of B from an integer, instead it would have to be done explicitly, like this:
B ob(5);
On a side note, your constructor taking an int as parameter, is not a default constructor, a default constructor is a constructor which can be called with no arguments.
You are not taking the assignment operator into account. Since you have not defined your own operator=() implementation, the compiler generates a default operator=(const B&) implementation instead.
Thus, your code is effectively doing the following logic:
#include <iostream>
using namespace std;
class B
{
public:
B(int x) //custom constructor
{
cout << "Constructor called" << endl;
}
B(const B &b) //copy constructor
{
cout << "Copy constructor called" << endl;
}
B& operator=(const B &b) //default assignment operator
{
return *this;
}
};
int main()
{
B ob(5);
ob.operator=(B(6));
ob.operator=(B(7));
return 0;
}
The compiler-generated operator=() operator expects a B object as input, but you are passing an int value instead. Since B has a non-explicit constructor that accepts an int as input, the compiler is free to perform an implicit conversion from int to B using a temporary object.
That is why you are seeing your constructor invoked three times - the two assignments are creating temporary B objects.
#include <string>
#include <iostream>
#include <utility>
struct A {
std::string s;
A() : s("test") {}
A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
A(A&& o) : s(std::move(o.s)) {}
A& operator=(const A&) { std::cout << "copy assigned\n"; return *this; }
A& operator=(A&& other) {
s = std::move(other.s);
std::cout << "move assigned\n";`enter code here`
return *this;
}
};
A f(A a) { return a; }
struct B : A {
std::string s2;
int n;
// implicit move assignment operator B& B::operator=(B&&)
// calls A's move assignment operator
// calls s2's move assignment operator
// and makes a bitwise copy of n
};
struct C : B {
~C() {}; // destructor prevents implicit move assignment
};
struct D : B {
D() {}
~D() {}; // destructor would prevent implicit move assignment
//D& operator=(D&&) = default; // force a move assignment anyway
};
int main()
{
A a1, a2;
std::cout << "Trying to move-assign A from rvalue temporary\n";
a1 = f(A()); // move-assignment from rvalue temporary
std::cout << "Trying to move-assign A from xvalue\n";
a2 = std::move(a1); // move-assignment from xvalue
std::cout << "Trying to move-assign B\n";
B b1, b2;
std::cout << "Before move, b1.s = \"" << b1.s << "\"\n";
b2 = std::move(b1); // calls implicit move assignment
std::cout << "After move, b1.s = \"" << b1.s << "\"\n";
std::cout << "Trying to move-assign C\n";
C c1, c2;
c2 = std::move(c1); // calls the copy assignment operator
std::cout << "Trying to move-assign D\n";
D d1, d2;
// d2 = std::move(d1);
}
While executing a2 = std::move(a1) statement, the behaviour is different from executing the statement b2 = std::move(b1). In the below statement the b1.s is not becoming empty after the move operation while a1.s is becoming empty after move operation.
Can anyone tell what exactly is happening there?
One of the great (and constant) misconceptions about C++11 and rvalue references is that std::move does something to an object (or something on that order).
It doesn't. std::move really just casts its parameter to rvalue reference type and returns that. Anything done to the object happens in the move constructor, move assignment operator (etc.) based on the fact that the version that takes an rvalue reference is invoked (instead of one taking a value or lvalue reference).
As far as the specific question you asked goes, at least based on the comments in your code, you seem to have some misunderstandings. The comment on a2=std::move(a1); says you're doing a "move assignment from an xvalue". That's...misleading at best. An xvalue is a value that's going to eXpire immediately. It's pretty much for the return value from a function:
Foo &&bar() {
Foo f;
// ...
return f;
}
In this case, bar() is an xvalue because bar returns an rvalue reference to an object that expires (goes out of scope) as function finishes execution.
As far as the specific question you asked goes, I suspect it mostly comes down to a question of whether (and if so, exactly how) your standard library implements the move constructor for std::string. Just for example, when using g++ (4.9.1) I get the same result you do--b1.s contains test both before and after being used as the source of a move. On the other hand, if I use MS VC++ 14 CTP, I get b1.s="test" before the move and b1.s="" after the move. Although I haven't tested it, I'd expect results with Clang to be the same. In short, it looks like gcc's standard library doesn't really implement move assignment/construction for std::string (yet--at least as of v 4.9--I haven't looked at 5.0 yet).
Usually move assignment is implemented as a swap on std::string, so why should the string become empty since it's always initialized with "test"?
Where do you see that a1.s is becoming empty since there is no print of it?
I don't see any strange behavior here. Both are treated in the same way.