SLet's take this class:
class standardClass
{
public:
standardClass(int) {}
~standardClass() {}
standardClass(standardClass&) {}
standardClass & operator=(standardClass&)
{
return *this;
}
};
int main()
{
standardClass stdClassObj1(1);
standardClass stdClassObj2(stdClassObj1);
standardClass stdClassObj3 = stdClassObj2;
stdClassObj1 = stdClassObj2;
stdClassObj2 = standardClass(4);
stdClassObj2 = 4;
}
I am getting an error on the last assignment.
All these lines are allowed except the last one. In the last one the integer 4 does not invoke the constructor. The error says there is no assignment operator for this. I understand that. But what I'm confused is why this implicit conversion works for a new object (line 1) and not to an existing object (line 2).
I do understand that in the case of line 1, copy constructor is called to create the new AObj4. But in line 2 it invokes the assingment operator. Why cant it make a temporary object with integer 4 and invoke the assignment operator as it would do for line 3?
standardClass(standardClass &objToCopy) // copy constructor
A copy constructor should take a const-qualified reference, like so:
standardClass(const standardClass &objToCopy)
Same for your assignment operator: that should be
standardClass & operator=(const standardClass &objToCopy)
Non-const-qualified lvalue references cannot be used with temporary objects, such as the temporary object that would otherwise be created from your literal 4.
You just changed the entire class. The problem is that you have:
standardClass & operator=(standardClass &objToCopy) // assignment operator
{
cout << "Copy assignment operator: " << objID << endl;
objID = objToCopy.objID;
return *this;
}
4 cannot be converted to a standardClass &.
If you use:
standardClass & operator=(standardClass const& objToCopy)
// ^^^^^^
{
cout << "Copy assignment operator: " << objID << endl;
objID = objToCopy.objID;
return *this;
}
all will be ok.
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.
Could you explain me the output of the following code? The variable changes it's value at the end of assignment.
#include <iostream>
#include <new>
using namespace std;
template<class CRTP>
class object_t {
public:
object_t<CRTP> &operator=(const object_t<CRTP> &a) {
((CRTP*)this)->~CRTP();
new ((CRTP*)this) CRTP(*(const CRTP*)&a);
cout << "Value in assignment: " << ((CRTP*)this)->n << endl;
return *this;
}
};
class darr_t : public object_t<darr_t> {
public:
int n;
darr_t(const darr_t &a) : n(a.n + 1) {
cout << "Value in constructor: " << n << endl;
}
darr_t(int pn) : n(pn) {}
};
int main() {
darr_t tmp(42);
darr_t tmp2(55);
tmp = tmp2;
cout << "Value in main: " << tmp.n << endl;
return 0;
}
Output:
Value in constructor: 56
Value in assignment: 56
Value in main: 55
Expected output:
Value in constructor: 56
Value in assignment: 56
Value in main: 56
Edit:
Thanks to #Cheersandhth.-Alf and #Mr_Hic-up answers!
The problem is that default darr_t::operator= first calls base type's assignment but after that it calls assignment(overrides) for members of darr_t object!
You are observing the behavior because:
The compiler defines an implicit copy assignment operator for darr_t.
The implicit copy assignment calls the copy assignment operator of the base class first before performing copy assignment of the member variables.
Here's relevant documentation from http://en.cppreference.com/w/cpp/language/as_operator:
Implicitly-declared copy assignment operator
If no user-defined copy assignment operators are provided for a class type (struct, class, or union), the compiler will always declare one as an inline public member of the class. This implicitly-declared copy assignment operator has the form T& T::operator=(const T&) if all of the following is true:
each direct base B of T has a copy assignment operator whose parameters are B or const B& or const volatile B&
each non-static data member M of T of class type or array of class type has a copy assignment operator whose parameters are M or const M& or const volatile M&
Otherwise the implicitly-declared copy assignment operator is declared as T& T::operator=(T&). (Note that due to these rules, the implicitly-declared copy assignment operator cannot bind to a volatile lvalue argument)
new ((CRTP*)this) CRTP(*(const CRTP*)&a);
That pointer magic you're trying to pull isn't doing what you expect it to. Adding some lines to print out the addresses of the object before printing the variable yields (on my machine):
Address in constructor: 0x7fff56264610
Value in constructor: 56
Address in assignment: 0x7fff56264610
Value in assignment: 56
Address in main: 0x7fff56264618
Value in main: 55
I have conflict in below codes.
#include <iostream>
using std::cout;
using std::endl;
class TestApp {
public:
TestApp(int _x = 9) {
cout << "default constructor\n";
}
TestApp(const TestApp &app) {
cout << "Copy constructor\n";
}
~TestApp() {
cout << "destructor\n";
}
void setX(int _x) {
}
const TestApp &operator=(TestApp &obj) {
cout << "= operator\n";
return *this;
}
void setX(int _x) {
cout << "Inside setX\n";
}
};
int main() {
TestApp app1;
TestApp app2;
TestApp app3;
(app1 = app2) = app3; // 1
app1 = app2 = app3; // 2
app1.setX(3)
return 0;
}
I got this error: for line 1 main.cpp:38: error: passing ‘const TestApp’ as ‘this’ argument of ‘const TestApp& TestApp::operator=(TestApp&)’ discards qualifiers
However I can use app1.setX(3);
main.cpp:38: error: no match for ‘operator=’ in ‘app1 = app2.TestApp::operator=(((TestApp&)(& app3)))’
main.cpp:28: note: candidates are: const TestApp& TestApp::operator=(TestApp&)
and to make it working I should make operator= like:
TestApp &operator=(const TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 1
TestApp &operator=(TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 2
why if I remove const keyword it works? and after line 1 app1 object is not constant.
You may not assign constant objects. For example consider this simple code
const int x = 10;
x = 20;
The compiler will issue an error for the second statement because x is a constant object and may not be assigned.
The same is valid for statement
(app1 = app2) = app3;
here expression (app1 = app2) returns constant reference that may not be assigned,
A constant reference does not mean that the object itself that it refers to is constant. Consider the following example
int x = 10;
const int &rx = x;
x = 20;
rx = 30;
Though rx is defined as constant reference you may change the object x itself. You may not use the reference to assign object x, so the compiler will issue an error for the last statement.
We use constant references very often in parameter declarations of functions that to prevent changing of objects they refer to inside the functions. For example
void f( const int &x )
{
x = 20; // compilation error
}
int x = 10;
f( x );
So defining a constant reference to a non-constant object does not makes the object itself constant. It only prevents to change the object using this reference.
And you need to define only one copy assignment operator
TestApp &operator=(const TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 1
There is no any need to define the copy assignment operator as
TestApp &operator=(TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 2
if you are not going to change the right operand. So it is better to define it as a constant reference const TestApp &obj
Of course you may have these two operators together but there is no any sense to have the second operator.
On the other hand you may not have only the second operator. In this case you will be unable to use constant objects that assign them to other objects.
The correct way to provide an assignment operator is to declare it as follows:
TestApp &operator=(const TestApp &obj) {
cout << "= operator\n";
return *this;
}
Note that there is only one const in front of the right hand side operand, the operator itself and its return value are not declared const.
It is wrong to declare the operator const because the assignment operator is meant to modify the this object.
And it unnecessarily constrains the use of the operator to return a const reference, because the caller already provided you with a non-const reference. Consequently, the caller already has non-const access to the object, so returning a const reference unnecessarily stops him from reusing the return value in a non-const context
This is what happens when you do the double assignment app1 = app2 = app3;: It is evaluated as app1 = (app2 = app3);, so the return value of one assignment is passed as the right hand side parameter to the next assignment. The non-const reference returned by the first assignment can implicitly be converted to a const reference, so this works fine.
If your compiler complains about your line 2 with the declaration given above, then your compiler is to blame. I have checked the following code with gcc 4.7.2, and it works fine:
class Foo {
public:
Foo() {};
Foo(const Foo& other) {}
Foo& operator=(const Foo& other) { return *this; }
};
int main() {
Foo foo1, foo2, foo3;
(foo1 = foo2) = foo3;
foo1 = foo2 = foo3;
}
To understand constructor and assignment, I wrote a very simply testing code like this:
class A {
public:
A() { std::cout<<"This is default cstr."; }
A(int i) { std::cout<<"This is int cstr. value is "<<i; }
A(const A &a) { std::cout<<"This is copy cstr."; }
A operator=(const A &a) { std::cout<<"This is assignment operator."; return *this;// this line is tricky }
};
int _tmain(int argc, _TCHAR* argv[]) {
std::cout<<"line 1 "; A a1; std::cout<<std::endl;
std::cout<<"line 2 "; A a2 = A(1); std::cout<<std::endl;
std::cout<<"line 3 "; a1 = a2; std::cout<<std::endl;
return 0;
}
For line 3 I got:
line 3 This is assignment operator.This is copy cstr.
But if I change return *this; to return NULL, I got:
line 3 This is assignment operator.This is int cstr. value is 0
Could someone explain what happened inside for me?
Your operator is returning A instead of A&:
A operator=(const A &a)
So when you return NULL, you are calling the implicit constructor A(int) and passing NULL to it.
The problem
line 3 This is assignment operator.This is copy cstr.
Your code's calling:
A operator=(const A &a) { std::cout<<"This is assignment operator."; return *this;
This obviously prints "This is assignment operator.", then return *this; statement sees the return type of A and creates a return value of type A doing the equivalent of A(*this); -> that calls the copy constructor, explaining this part of the output:
line 3 This is assignment operator.This is copy cstr.
^^^^^^^^^^^^^^^^^^
But if I change return *this; to return NULL, I got:
line 3 This is assignment operator.This is int cstr. value is 0
In this case:
A operator=(const A &a) { std::cout<<"This is assignment operator."; return NULL; }
You end up creating the return value of type A as per A(NULL), and as NULL is 0, that matches the A(int) constructor best, which is why you see:
line 3 This is assignment operator.This is int cstr. value is 0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The solution
A& operator=(const A &a) { std::cout<<"This is assignment operator."; return *this; }
^
You normally want the assignment operator to return a reference to the *this object. That way, no additional A object is constructed as the assignment operator function returns.
Aside - why return `A&` anyway?
The reason A& is returned and not void, is that it allows further chained use of the object, as in:
a1 = a2 = a3;
Which is evaluated as:
a1.operator=(a2.operator=(a3));
Is a2.operator= returned void then there'd be no usable argument to a1.operator=().
A non-const reference supports usage like:
make_uppercase(my_string = other_string);
In some other languages, that would need to be broken into two statements. Whether you wish it was depends on whether you find it confusing, and how much you value concision.
Your code says
A operator = (const A& a)
You take a reference to an A, you modify yourself, and then you return A(*this) which invokes the copy-constructor to create a new instance and return by value.
What you probably intended was
A& operator = (const A& a)
This will then return a reference to *this rather needing to copy it into a new temporary instance.
Be aware that NULL is a macro alias for '0UL' or '0ULL' which the compiler detects as being a match for A(int). This is one of the reasons that C++11 introduced nullptr as an alternative to NULL.