#include <iostream>
using namespace std;
class Test {
public:
Test() {
cout << "Default constructor called." << endl;
}
Test(Test &obj) {
cout << "copy constructor called." << endl;
}
Test& operator=(const Test &obj) {
cout << "copy assignment called." << endl;
return *this;
}
~Test() {
cout << "destructor called." << endl;
}
};
Test func(Test test) {
cout << "func called" << endl;
return test;
}
int main(int argc, char* argv[]) {
Test t1;
Test t2 = func(t1); // No matching constructor for initialization for 'Test'
return 0;
}
I am learning c++. I wrote some test code to understand copy initialization and copy assignment. Now I can not figure out why the second assignment can not work.
An rvalue cannot be modified, and should, in this case, be treated as a const reference const Test& obj. So when you convert t1 into the test in func, it's OK because t1 is an lvalue, but not when you're constucting t2 from the return value, which is an xvalue (categorized into rvalue).
In short, the signature of your copy constructor is wrong because it only accepts lvalues.
The following patch on line 10 makes the code work for me.
Test(const Test &obj) {
^~~~~
Here's the article about copy constructor on cppreference.com. See Syntax section.
Also the rvalue article, which reads
An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
P.S. You can use move semantics (C++11), too, which accept rvalues only. It's not hard to write a move constructor like this:
Test(Test&& obj) ...
Your copy constructor allows the object being copied to be modified.
The statement
Test t2 = func(t1);
will store the return value of func() in a temporary, and then copy that into t2. However, that requires a copy constructor that accepts a const reference, since a temporary cannot be bound to a non-const reference.
(Technically, the compiler is allowed to elide the temporary, but it is still required to issue diagnostics for your code assuming it has created the temporary. In other words, the copy constructor must be const).
Change your copy constructor to accept a const reference.
Test(Test const &obj) {
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);
};
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.
Is it ok that the following code
#include <iostream>
struct Foo
{
Foo() { std::cout << "Foo::Foo" << std::endl; }
~Foo() { std::cout << "Foo::~Foo" << std::endl; }
Foo(const Foo&) { std::cout << "Foo::Foo(const Foo&)" << std::endl; }
Foo& operator=(const Foo&) { std::cout << "Foo::operator=(const Foo&)" << std::endl; return *this; }
Foo(Foo&&) { std::cout << "Foo::Foo(Foo&&)" << std::endl; }
Foo& operator=(Foo&&) { std::cout << "Foo::operator=(Foo&&)" << std::endl; return *this; }
};
Foo foo()
{
Foo second;
return second;
}
int main()
{
foo();
}
produces such output:
Foo::Foo
Foo::Foo(Foo&&)
Foo::~Foo
Foo::~Foo
Why does it call move constructor instead of the copy constructor?
The answer is simply because the standard says so. Section 12.8, paragraph 32:
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the
object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly
parenthesized) id-expression that names an object with automatic storage duration declared in the body or
parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution
to select the constructor for the copy is first performed as if the object were designated by an rvalue.
And that's basically all there is to it. Logically, if the variable has automatic storage duration, and you return it, then immediately after the return, the variable will not exist any more when the local scope cleans up. That is why, even though it is an lvalue, it is quite similar to an rvalue. Because it's still technically an lvalue, this exception has to be made explicitly in the standard, and here we are. Note that this whole situation is specific to returning from a function, and does not include scope ending in general.
second is returned from foo, and, being a local object, is treated as an rvalue (thanks to Nir Friedman for the correct explanation). The returned Foo object is constructed from a temporary, which binds to the move constructor.
Modern compiler should skip the move constructor call and use copy elision (demo).
This question already has answers here:
On how to recognize Rvalue or Lvalue reference and if-it-has-a-name rule
(3 answers)
Closed 6 years ago.
class MyClass {
public:
MyClass()
{
std::cout << "default constructor\n";
}
MyClass(MyClass& a)
{
std::cout << "copy constructor\n";
}
MyClass(MyClass&& b)
{
std::cout << "move constructor\n";
}
};
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(temp); // calls COPY constructor... not expected...?
}
int main()
{
test(MyClass{});
return 0;
}
For the above code, I expected both object creations in test() to call move constructor because b is a r-value reference type (MyClass&&).
However, passing b to the MyClasss constructor does not call move constructor as expected, but calls copy constructor instead.
Why does second case calls copy constructor even if the passed argument is of type MyClass&& (r-value reference)???
I am using gcc 5.2.1. To reproduce my results, you must pass -fno-elide-constructors option to gcc to disable copy elision optimization.
void test(MyClass&& temp)
{
if (std::is_rvalue_reference<decltype(temp)>::value)
std::cout << "temp is rvalue reference!!\n";
else
std::cout << "temp is NOT rvalue!!\n";
}
Above code prints out "temp is rvalue reference" even if temp is named.
temp's type is rvalue reference type.
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(temp); // calls COPY constructor... not expected...?
}
That is because, temp (since it has a name,) is an lvalue reference to an rvalue. To treat as an rvalue at any point, call on std::move
void test(MyClass&& temp)
{
MyClass a(MyClass{}); // calls MOVE constructor as expected
MyClass b(std::move(temp)); // calls MOVE constructor... :-)
}
#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.