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.
Related
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";
}
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.
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.
This is my code:
#include <iostream>
using namespace std;
class A
{
int i;
public:
A(int v) : i(v) { }
A(const A& r) : i(r.i) {
cout << "Copy constructor" << endl;
}
A operator=(const A& r) {
cout << "Assignment function" << endl;
return r;
}
void show() {
cout << i << endl;
}
};
int main()
{
A a(1);
A b(2);
a = b;
a.show();
return 0;
}
Value of b is 2 and value of a is 1. In 'main', b is copied into a and this the output I get:
Assignment function
Copy constructor
This is understandable, but the output for a.show() comes out be 1. I can't understand this. How? Because b is copied into a using the copy constructor so shouldn't a.i have the value of b.i?
b is copied into a using the assignment operator, not the copy constructor. And your assignment operator doesn't assign i, so a.i keeps its original value.
The copy constructor you are seeing is for the return value of operator=. It is more customary to return the left-hand-side by reference, not the right-hand-side by value:
A& operator=(const A& r) {
cout << "Assignment function" << endl;
i = r.i;
return *this;
}
When you define the assignment operator you have to do all of the work to copy the data.
A operator=(const A& r) {
cout << "Assignment function" << endl;
i = r.i;
return r;
}
so a = b is calling your assignment operator (obviously, hence your output). But right now it isn't copying anything.
The assignment operator doesn't do anything to the "assigned to" object. The copy constructor call you see reported is from the creation of the assignment return value.
Your copy assignment should probably look loke this:
A& A::operator= (A const& other) {
// output
this->i = other.i;
return *this;
}
This question already has answers here:
Copy constructor elision?
(2 answers)
Closed 8 years ago.
class A
{
public:
A ()
{
wcout << L"Empty constructed." << endl;
}
A (LPCWSTR Name)
: m_Name(Name)
{
wcout << L"Constructed." << endl;
}
friend void swap (A& Lhs, A& Rhs)
{
using std::swap;
swap(Lhs.m_Name, Rhs.m_Name);
}
A (A&& Other)
{
wcout << L"Move constructed." << endl;
swap(*this, Other);
}
A (const A& Other)
: m_Name(Other.m_Name)
{
wcout << L"Copy constructed." << endl;
}
A& operator= (A Other)
{
wcout << L"Assignment." << endl;
swap(*this, Other);
return *this;
}
~A ()
{
wcout << L"Destroyed: " << m_Name.GetString() << endl;
}
private:
CString m_Name;
};
int
wmain ()
{
A a;
a = A(L"Name"); // Where is the construction of this temp object?
return 0;
}
This is the output I get for the above code:
Empty constructed.
Constructed.
Assignment.
Destroyed:
Destroyed: Name
See the line with the comment. What I expected is for a temp object to get constructed there, and the argument Other in the operator= would get move-constructed from that temp-object. What's happening here?
The output that says "Constructed" is actually the feedback from the construction of that temporary object.
If you are looking for an additional copy-construction (or move-construction) of Other parameter of copy-assignment operator, it was probably eliminated by copy elision. Your A(L"Name") is immediately constructed and used as that Other parameter. No extra copying (or moving) is performed.
You can use an interactive debugger to see for yourself. However, your answer to where "Name" got constructed is this:
A (LPCWSTR Name)
: m_Name(Name)
{
wcout << L"Constructed." << endl;
}
and
a = A(L"Name");
Your code constructed an empty object at the code line A a;.
It then constructed "Name".
Then it swapped the two's CString m_Name; (shown by the output Assignment).
Then it destructed the original object holding "Name" (A(L"Name")).
Then it destructed the original empty object that is now holding "Name" in its m_Name.