return a const reference to an object - c++

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;
}

Related

What is the meaning of assigning to return value of getter taking this pointer by value?

This doubt came to me when I jumped on an existing code and mistakenly used a getter to set a property,
obj.getProp() = otherProp;
instead of calling the setter,
obj.setProp(otherProp);
I did not realize the mistake because there was no error at compilation or runtime; the assignment resulted in a no-op.
So I came up with the following example, which outputs 337:
#include <iostream>
struct A {
int x = 0;
A(int x) : x(x) {}
A(A& a) : x(a.x) {}
void operator=(A const& other) { x = other.x; }
};
struct B {
A a{3};
int x{3};
A getAbyVal() { return a; }
A& getAbyRef() { return a; }
int getXbyVal() { return x; }
};
int main() {
B b;
std::cout << b.a.x; // this and the other two cout print what I expect, but...
b.getAbyVal() = A{7}; // ... I expected this to fail at compilation time in the first place...
//b.getXbyVal() = 3; // ... just like this fails.
std::cout << b.a.x;
b.getAbyRef() = A{7};
std::cout << b.a.x;
}
So my question is two folds:
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so that the former compiles and the latter doesn't (beside the fact that the types are A and int)?
changing void operator=(A const& other) { x = other.x; } to void operator=(A const& other) & { x = other.x; } makes b.getAbyVal() = A{7}; fail to compile. Why is this the case?
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so
that the former compiles and the latter doesn't (beside the fact that
the types are A and int)?
Surprisingly, the difference in types is exactly what makes one compile correctly, and other to fail.
A has an assignment operator defined for it, so compiler dutifully invokes it on the return value (only to discard the whole object later). But the code you wrote supports this. From the compiler view, some other interesting things might have happened in your assignment operator, despite the fact that the object will be eradicated (side effects in formal parlance).
With int as a return value, compiler knows there are no side effects of assigning value to int, so assigning any value to object which is to be eradicated immediately does not make any sense.
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so that the former compiles and the latter doesn't (beside the fact that the types are A and int)?
The difference is precisely that one functions returns a class type, and the other function returns a POD type. A temporary int, for example can't be assigned to:
42 = x; // error
so similarly the language disallows assigning to a temporary int returned from a function as well. This is not the default behavior for user-defined class types so assigning to a temporary A compiles:
A{} = x; // ok
changing void operator=(A const& other) { x = other.x; } to void operator=(A const& other) & { x = other.x; } makes b.getAbyVal() = A{7}; fail to compile. Why is this the case?
Adding a & at the end of is called a ref-qualifier, and allows a user-defined class to have the same semantics as a POD type when it comes to assigning to a temporary. Adding a & at the end of operator= constrains it to only be used on an l-value (basically, a named variable, or a reference returned from a function).
A{} = x; // now error
A a;
a = x; // still ok

How to copy "partially" an object using a std::function?

Here's the code I have:
#include <iostream>
#include <functional>
struct Test {
struct Envelope {
const int x = 1;
int y = 2;
int z = 3;
};
Envelope mEnvelope;
struct Buffer {
Envelope mEnvelope;
} mBuffer;
std::function<Buffer()> func{[this] {
mBuffer.mEnvelope = mEnvelope;
return mBuffer;
}};
};
int main() {
Test test;
}
it says:
g++ -std=c++17 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In lambda function:
main.cpp:17:29: error: use of deleted function 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)'
17 | mBuffer.mEnvelope = mEnvelope;
| ^~~~~~~~~
main.cpp:5:12: note: 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)' is implicitly deleted because the default definition would be ill-formed:
5 | struct Envelope {
| ^~~~~~~~
main.cpp:5:12: error: non-static const member 'const int Test::Envelope::x', can't use default assignment operator
I've tried using a Copy Constructor:
Envelope(const Envelope &other) {
y = other.y;
}
or override the operator =
Envelope &operator=(Envelope &other) {
// self-assignment guard
if (this == &other) {
return *this;
}
y = other.y;
}
But the errors grown even more.
I need to copy only some "part" of the object.
This is just a test, of course the real object have lots of members fields, and some need to be ignored.
How to do it within a std::function<Buffer()>?
OK, since it might not be obvious what the problem is:
A copy assignment operator should usually have a const& parameter, not a & parameter.
You should then also provide the copy constructor of Envelope as you showed. First of all it has different behavior than the implicitly-generated one (which would copy over all the elements, not only y) and the generation of the implicit copy constructor when there is a user-defined copy assignment has been deprecated since C++11 and will probably be removed in a future standard iteration.
Then you will need to default the default constructor as well, since you have a user-defined constructor now:
Envelope() = default;
Furthermore, your copy assignment operator is declared to return a Envelope& (as it should), but you forgot to actually put a return statement in it at its end, so executing it will cause undefined behavior as is:
return *this;
You can create your own copy ctor/operator in order to only copy the info you want:
#include <iostream>
#include <functional>
struct Test {
typedef struct {
const int x;
int y;
int z;
} Envelope_t;
public:
Test():env({1,2,3}){}
Test(const Test & copy):env({copy.env.x,5,copy.env.z}) {}
Test& operator=(const Test& copy){
env.y=copy.env.y+7;
env.z=copy.env.z;
return *this;
}
void printValues() {
std::cout << "x:" << env.x << "\ny:" <<
env.y << "\nz:" << env.z << "\n\n";
}
private:
Envelope_t env;
};
int main() {
Test original;
original.printValues();
Test copyCtor(original);
copyCtor.printValues();
Test copyOp;
copyOp = copyCtor;
copyOp.printValues();
}

What is the difference between rvalue reference and lvalue reference?

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.

Overloading of = operator for a class

I was recently refreshing my C++ knowledge in operator overloading. As recommended I return a reference to *this for the operator overload of '='. But then I found one problem :-
#include <iostream>
using namespace std;
class MyClass
{
int num = 4;
public:
MyClass() = default;
MyClass(int x) : num(x) {}
void getnum(int x)
{
num = x;
}
void shownum()
{
cout << num << '\n';
}
MyClass& operator = (const MyClass& obj) // works even without const
{
this->num = obj.num;
return *this;
}
~MyClass() = default;
};
int main()
{
MyClass x, y(5), z(7);
z = MyClass(8) = y; // temporary object return reference
x.shownum();
y.shownum();
z.shownum();
}
This code doesnt result into a UB even though a temporary object MyClass(8) is involved in between which will be equated with y (fine no problem) but then it's reference will be sent to equate with z. Why doesn't the dangling reference problem occur here ? Why does the overload function work without const MyClass&' on the temporary object 'MyClass(8) ?
Temporaries live until the full expression ends. At the end the temporary goes away, but by then the assignments have all been performed and since you don't store the reference anywhere it's okay for the temporary to go away.
The first assignment MyClass(8) = y; returns a MyClass& that doesn't tell anything about being a temporary. And so it matches the parameter of the next operator=, whether that one expects a const or non-const parameter.
If you try to just assign a temporary z = MyClass(8);, you will see that it now requires the parameter to be a const reference.
Also, there are no dangling references here, because temporary objects live until the end of the full expression (usually at the ;), and no pointers or references are saved anywhere. Just copies of the num values.

Overloading Based on L-Value versus R-Value

I found in a C++ book the following:
Although we will not be doing it in this book, you can overload a
function name (or operator) so that it behaves differently when used
as an l-value and when it is used as an r-value. (Recall that an
l-value means it can be used on the left-hand side of an assignment
statement.) For example, if you want a function f to behave
differently depending on whether it is used as an l-value or an
r-value, you can do so as follows:
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
I tried this and it didn't work:
class Foo {
public:
int& id(int& a);
const int& id(int& a) const;
};
int main() {
int a;
Foo f;
f.id(a) = 2;
a = f.id(a);
cout << f.id(a) << endl;
}
int& Foo :: id(int& a) {
cout << "Bar\n";
return a;
}
const int& Foo :: id(int& a) const {
cout << "No bar !\n";
return a;
}
Have I wrongly understood it ?
Either the book's example is flat-out wrong, or you copied the wrong example from the book.
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
With this code, when you call s.f() where s is an object of type SomeClass, the first version will be called when s is non-const, and the second version will be called when s is const. Value category has nothing to do with it.
Ref-qualification looks like this:
#include <iostream>
class SomeClass {
public:
int f() & { std::cout << "lvalue\n"; }
int f() && { std::cout << "rvalue\n"; }
};
int main() {
SomeClass s; s.f(); // prints "lvalue"
SomeClass{}.f(); // prints "rvalue"
}
Ofcourse the book is correct. Let me explain the workings of an example of what the author meant :
#include <iostream>
using namespace std;
class CO
{
int _m;
public:
CO(int m) : _m(m) {}
int& m() { return _m; } // used as an l-value
int const& m() const { return _m; } // used as an r-value
};
int main()
{
CO a(1);
cout << a.m() << endl;
a.m() = 2; // here used as an l-value / overload resolution selects the correct one
cout << a.m() << endl;
return 0;
}
Output is
1
2
What you misunderstood is the function signature. You see when you have an argument &arg (as in id(&arg)) you pretty much predefine the l-valuness of it, so returning it through a const or non const member function does not change a thing.
The author refers to a common writting style that allows for 'getters' and 'setters' to be declared with a signature different only in const qualifires yet compile and behave correctly.
Edit
To be more pedantic, the following phrase
Recall that an l-value means it can be used on the left-hand side of an assignment statement.
is not valid anymore. lr valuness applies to expressions, and the shortest way to explain it, is that an expression whose adress we can take, is an l-value; if it's not obtainable it's an r-value.
So the syntax to which the author refers to, enforces the member function to be used correctly (correct compilation / overload resolution) at both sides of the assignment operator. This nowdays is no longer relevant to lr valueness.
A const member function can only be called on a const object. It makes no difference what you do with the return value. In your example, f is non-const, so it always calls the non-const version of f(). Note that you can also overload on r-value references (&&) in C++11.