Unable to understand overloading of assignment operator - c++

To understand the working of objects in c++ better, I wrote this piece of code:
using namespace std;
char n[] = "\n";
class T
{
private:
int num;
public:
T ()
{
num = 0;
cout << n << (long)this % 0xFF << " created without param";
}
T (const int param)
{
num = param;
cout << n << (long)this % 0xFF << " created with param = " << param;
}
T (const T& obj)
{
num = obj.num;
cout << n << (long)this % 0xFF << " created as copy of " << (long)&obj % 0xFF;
}
const T& operator= (const T& obj)
{
if (this == &obj)
return *this;
num = obj.num;
cout << n << (long)this % 0xFF << " got assigned the data of " << (long)&obj % 0xFF;
return *this;
}
~T ()
{
cout << n << (long)this % 0xFF << " destroyed";
}
int get () const {return num;}
void set (const int param) {num = param;}
};
T PlusTen (T obj)
{
T newObj(5);
newObj.set( obj.get() +10 );
return newObj;
}
int main ()
{
T a, b(4);
a = b;
a = PlusTen(b);
cout << n;
return 0;
}
Its working fine, but when I remove the const qualifier in "return-type" and "parameter" of overloaded assignment operator like this below:
T& operator= (T& obj) // const removed
{
if (this == &obj)
return *this;
num = obj.num;
cout << n << (long)this % 0xFF << " got assigned the data of " << (long)&obj % 0xFF;
return *this;
}
Then this line of main function gives error:
a = PlusTen(b);
The error message being:
no match for 'operator=' (operand types are 'T' and 'T')
note:
candidate is: T& T::operator=(T&)
no known conversion for argument 1 from 'T' to 'T&'
If operand types of 'T' and 'T' are problematic, how come the line just above it (a = b;) is totally fine? They too are of operand types 'T' and 'T' !!
I found a relevant questoin here but no useful detail there:
why must you provide the keyword const in operator overloads
One person there says that if we don't use const in operator=, we can only use it for non-const objects. But in my case too both sides are non-const. Then why error? Especially when the line just above it, which is identical in operand types, compiles fine?
Compiler used: MinGW

PlusTen(b); is creating a temporary object. Since non-const references can not be bound to temporary objects, operator= can not be called here.
In a = b; b is not a temporary, it's a modifiable object (a so-called l-value). Non-const reference is successfully bound to it, and operator= is called.
For the extra fun, try defining your b as following:
const T b(4);

This function
T PlusTen (T obj)
{
T newObj(5);
newObj.set( obj.get() +10 );
return newObj;
}
returns a temporary object of type T. this temporary object can be bound with a constant reference.
This is important! This is the reason OP is confused!
Non-const references to temporary objects are not allowed in C++ !! In case of a = PlusTen(b);, as PlusTen(b) is a temporary value, the function operator= cannot bind the argument obj to PlusTen(b) value, because obj is non-const whereas PlusTen(b) can only be const.
So the compiler issues an error because the parameter of the assignment operator
T& operator= (T& obj)
^^^^^^
is not a constant reference.
The qualifier const in the return type makes no matter in the context how the operator is used in your program.

Related

E0349 no operator "<<" matches these operands [duplicate]

This question already has answers here:
How come a non-const reference cannot bind to a temporary object?
(11 answers)
Error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
(2 answers)
cannot bind non-const lvalue reference of type to an rvalue of type
(1 answer)
Closed 3 months ago.
I try to overload operator << and ++ (post and pre).
This is part of my code, but I get error "e0349: no operator matches these operands".
Could you tell me where I made a mistake?
(C++, VS2022)
#include <iostream>
#include <string>
using namespace std;
class K {
int x, y;
public:
K(int a, int b) :x(a), y(b) {};
K() :x(0), y(0) {};
K operator++(int);
K& operator++();
friend ostream& operator<< (ostream & str, K & obj);
};
K K::operator++(int) {
K temp(*this);
x += 1;
y += 1;
return temp;
}
K& K::operator++() {
x += 1;
y += 1;
return *this;
}
ostream& operator<<(ostream& str, K& obj) {
str << "[" << obj.x << ", " << obj.y << "]";
return str;
}
int main(int argc, char* argv[])
{
K obj{10, 20};
cout << obj++ << endl; //here I get error
cout << obj << endl;
cout << ++obj << endl;
}
Simply the post-operator++ is written so that it returns a copy of the temporary value which can be used as rvalue but being that the signature of the insertion operator requires a reference of the value, it does not know how to retrieve the data passed as a copy. This is how you should modify the overloading function of the extract operator:
ostream& operator<<(ostream& str, K const& obj) {
str << "[" << obj.x << ", " << obj.y << "]";
return str;
}
You're simply telling the function that the passed value will have a constant reference that it won't change over time. So it is as if you are taking the reference of the copy value. I know it's very tricky as a thing but I should have cleared your minds enough

Is it valid to return reference for artimetic operator overloading?

As we know, we normally return a copy to a new class when overloading an arithmetic operator. As an example, << operator is normally defined like this:
T1 operator<<(const T1& a, const T2& b) {
// apply b
return a;
}
But I wonder if it is generally valid to return a reference in this case or not. For example, is it possible that following code creates any invalid situation?
T1& operator<<(T1& a, const T2& b) {
// apply b
return a;
}
Why I need this? Let's assume I have a code like this:
class B {
public:
B() {}
~B() { std::cout << ss_.str() << "\n"; }
template<typename T>
B& operator<<(T val) {
ss_ << val;
return *this;
}
private:
std::stringstream ss_;
};
int main() {
B{} << "str" << "abc" << 1;
B{} << "str2" << "abc2" << 2;
}
In this code, constructor and destructor of type T1 will be called only once which will be really suitable here. But if I return by value for the << operator, constructor and destructor will be called for each temporary rvalues which is not great for my use case.
Is it possible that returning by reference create invalid code for overloading these operators?
Yes, it's not only valid, it's a common idiom when overloading << for stream outputs. For example:
std::ostream& operator<<(std::ostream& os, const MyData& data) {
os << data.a << ',' << data.b << ',' << data.c;
return os;
}
The rule about returning a reference is to never return a reference to a function local variable. This also includes function parameters that are passed by value.
It gets a little trickier when dealing with reference parameters.
const T1& operator<<(const T1& a, const T2& b) {
// apply b
return a;
}
is problematic since a could be bound to a temporary. That would mean that you are returning a reference to an object that is going to end at the end of the full expression the function call is in, which could cause trouble.
Using
T1 operator<<(T1& a, const T2& b) {
// apply b
return a;
}
You stop that from happening as now a can only bind to an lvalue so you don't have to worry about it living past the full expression the function is called in.

overloading operator+ twice (inside and outside class)

Why am I allowed to do this, why there is no ambiguity complain, and why is the class' method chosen with prior to the other one?
class EX
{
public:
//...
void operator+(const EX& ref)
{
cout << "A";
}
};
void operator+(const EX& ref1, const EX& ref2)
{
cout << "B" << endl;
}
int main()
{
EX obj1{20};
EX obj2{30};
cout << "obj1 + obj2 = " << obj1 + obj2 << endl;
return 0;
}
I was expecting the global function operator+ to be called an "B" printed on the screen, instead "A" was printed.
Your member overload accepts only non-const instances of EX for the first operand while the free overload accepts both const and non-const instances. By overload resolution rules, the non-const wins out because it exactly matches the type being passed in.
You'll get an ambiguity error if you make the member overload const, indicating that *this is const:
void operator+(const EX& ref) const
{
cout << "A";
}

Getting garbage value while doing chaining of assignment of class objects, using assignment operator overloading which returns class object by value

In assignment operator overloading, If I return the object by reference like below
One& operator=( const One& obj).
Then program works fine.
But if when I return by value, like below
One operator=( const One& obj)
Then o1 gets garbage value. Can anybody explain why return by value does not work in assignment operator overloading?
class One
{
public:
One(int a1, int b1) {
a = a1; b = b1;
}
int a;
int b;
One operator=( const One& obj) {
cout<<"\nOperator= is called\n"; a = obj.a; b = obj.b;
}
};
int main()
{
One o1(5,5);
One o2(10,10);
One o3(15,15);
cout << "\no1.a=" << o1.a << ", o1.b=" << o1.b << endl;
o1 = o2 = o3;
cout << "\no1.a=" <<o1.a << ", o1.b=" << o1.b << endl;
cout << "\no2.a=" <<o2.a << ", o2.b=" << o2.b << endl;
cout << "\no3.a=" <<o3.a << ", o3.b=" << o3.b << endl;
return 0;
}
Output:
o1.a=-13360, o1.b=0
o2.a=15, o2.b=15
o3.a=15, o3.b=15
Why o1 object showing garbage value in case of return by value in assignment operator. It works fine when return by reference. why?
o1 = o2 = o3; is evaluated as o1 = (o2 = o3);
That requires o2 = o3 to return something, and o1 is set to that value. But your overload currently doesn't. (Formally it means that the behaviour of your code is undefined). If you rewrite to
One& operator=(const One& obj)
{
std::cout << "\nOperator= is called\n";
a = obj.a;
b = obj.b;
return *this; // i.e. return a reference to self.
}
then all will be well. That said, the cool cats will use
One& operator=(One obj/*pass by value to exploit compiler optimisations*/)
{
std::cout << "\nOperator= is called\n";
std::swap(*this, obj);
return *this;
}
Reference: What is the copy-and-swap idiom?
The code of the = operator should be this:
One & operator=(const One& obj)
{
cout << "\nOperator= is called\n";
a = obj.a;
b = obj.b;
return *this;
}
One operator=( const One& obj)
{ cout<<"\nOperator= is called\n"; a = obj.a; b = obj.b; }
You do not have any return expression in your function definition, resulting in undefined behavior. That's why the operator does not work as it should.
In case you are using the gcc compiler, I would suggest you enable the -Wall option, which would have produced a warning, indicating the failure. In case you are using sth different there's probably an equivalent option available.
You may also want to take a look at the Copy-and-Swap Idiom which provides an advanced insight in the creation of a properly working copy-assignment operator.

Which operator gets invoked upon object evaluation?

I would like to print the id of my object when the object is being evaluated (third line in my main function "one = two; should output "Object id: 2"). I know that the assignment operator is only invoked for the lvalue, a conversation operator to itself will never be called and I don't want to use the function call operator.
Any ideas how this can be done, which operator must I overload?
P.S.: Please ignore any saneness of the code, I only care about identifying the right to-be-overloaded operator.
#include <iostream>
class Object
{
public:
Object( int id ) : id_( id )
{
}
//Assignment operator only invoked for lvalue
Object& operator= (const Object& other)
{
std::cout << "Object id: " << id_ << std::endl;
return *this;
}
//Conversion operator to itself will never be called
operator Object() const
{
return *(this);
}
//Function call operator - not what I mean
Object operator()()
{
std::cout << "Object id: " << id_ << std::endl;
return *(this);
}
private:
int id_;
};
int main()
{
Object one(1);
Object two(2);
one = two;
one = two();
return 0;
}
The concept of "object evaluation" isn't really present in C++. An expression can be evaluated, of course, and operator overloading fits into this: foo = bar invokes any overloaded operator= which matches the operands (and in the case of operator= specifically, must be a member of the LHS class type). But it's the assignment that's being evaluated, not foo or bar (and note that in the context of the operator overload, these are present as pointers or references, so it's not inevitable that either one will be evaluated at all).
The appropriate solution here will depend on what it is you're trying to accomplish. But there's no one function that automatically fires whenever your code mentions bar.
#include <iostream>
class Object
{
public:
Object( int id ) : id_( id )
{
}
//Assignment operator only invoked for lvalue
Object& operator= (const Object& other)
{
std::cout << "Object id: " << other.id_ << std::endl;
return *this;
}
private:
int id_;
};
int main()
{
Object one(1);
Object two(2);
one = two;
return 0;
}
I tried this to see if Mat was right, turns out it compiles and prints "Object id: 2" as expected.
So, this line:
one = two;
Will call this function that you already have:
Object& operator= (const Object& other)
{
std::cout << "Object id: " << id_ << std::endl;
return *this;
}
However, let's note that there are two Object objects that are visible in that function.
Object& operator= (const Object& other)
{
std::cout << "Object(" << this->id_ << ")";
std::cout << " = ";
std::cout << "Object(" << other.id_ << ")";
std::cout << "\n";
return *this;
}
You can see a full example where we run this code at ideone.com.