Why don't overloaded "<<" and "++" work together? [duplicate] - c++

This question already has answers here:
What are the basic rules and idioms for operator overloading?
(8 answers)
Closed 1 year ago.
Firstly, i'm pretty new to C++ and OOP, so sorry if asking silly questions. So, here it is, I overloaded the "<<" and "++" (postfix and prefix) and they work fine alone. But they seem to not work when combined. I don't get why, both ++-s return a foo type object, so I thinked that "<<" should work fine...
#include <iostream>
using namespace std;
class foo
{
int x;
public:
foo()
{
x=10;
}
foo(const foo& f1)
{
this->x=f1.x;
cout<<"OK"; /// testing if it really works
}
foo operator ++ ()
{
++x;
return *this;
}
foo operator ++ (int)
{
x++;
return *this;
}
friend istream& operator>>(istream& in, foo& S);
friend ostream& operator<<(ostream& out, foo& S);
};
istream& operator>>(istream& in, foo& S)
{
in>>S.x;
return in;
}
ostream& operator<<(ostream& out, foo& S)
{
out<<S.x;
return out;
}
int main()
{
foo A, B, C;
cout<<A;
//cout<<++A; //error: cannot bind non-const lvalue reference of type 'foo&' to an rvalue of type 'foo'
//cout<<A++; //error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'foo')
return 0;
}

Your stream output overload should take your foo references as const. After all, they shouldn't be modifying the foo's passed in:
friend istream& operator<<(istream& in, const foo& S);
A non-const reference parameter (like your foo& S), must have an l-value passed to it. Since your increment operator returns an r-value, you're seeing the compiler error (which says exactly this). To be able to take both l and r-values, you need to make the change to a const-reference parameter above.
In addition, your prefix increment operator should return by reference:
foo& operator ++ ()
{
++x;
return *this;
}
For more on the basic rules and idioms for overloading, read here: https://stackoverflow.com/a/4421719/2602718

In addition to scohe001's answer, your postfix and prefix operators also need correction.
Prefix operator should return a reference to the object being incremented. You're returning a copy of *this instead. Your return type should be foo& as such:
foo& operator ++ ()
{
++x;
return *this;
}
And in the postfix operator, you need to first remember the state of the object, i.e. make a copy of the object before modification, and then modify the object, and finally return the unmodified copy, like this:
foo operator ++ (int)
{
foo temp(*this);
++x;
return temp;
}

Here's your code, fixed up. I added comments where I made changes.
#include <iostream>
using namespace std;
class foo {
int x;
public:
foo() { x = 10; }
foo(const foo& f1) {
this->x = f1.x;
cout << "OK"; /// testing if it really works
}
// Changed return type
foo& operator++() {
++x;
return *this;
}
// Re-wrote body
foo operator++(int) {
foo tmp = *this;
++(*this);
return tmp;
}
friend istream& operator>>(istream& in, foo& S);
friend ostream& operator<<(ostream& out, const foo& S); // Added const
};
istream& operator>>(istream& in, foo& S) {
in >> S.x;
return in;
}
// Matched added const
ostream& operator<<(ostream& out, const foo& S) {
out << S.x;
return out;
}
int main() {
foo A, B, C;
cout << A << '\n';
cout << ++A << '\n';
cout << A++ << '\n';
return 0;
}
Prefix ++ and postfix ++ behave differently. This is observed when testing the operators out on a regular int.
int x = 5;
std::cout << x++ << '\n';
std::cout << ++x << '\n';
You get the output:
5
7
Postfix increment returns the original value but still increments. Prefix increment returns the incremented value. This is why prefix needs to return a reference. It returns itself, and we do that with a reference.
So what happened in the short code example above was x had its original value (5) returned, but the postfix increment still incremented, so x had a value of 6. Then the prefix print ensured that the incremented x was returned and it printed 7.
The change to operator<<() is convention. When printing an object, I don't want to modify it, so I pass it as a reference to const.

Related

Lifetime of primitive/object value when it's returned from a function

class Foo {
private:
int num;
std::string str;
public:
Foo& operator=(const Foo& rhs) {
this->num = rhs.num;
this->str = rhs.str;
return *this;
}
Foo& operator+(const Foo& rhs) {
Foo tmp(this->num + rhs.num, this->str + rhs.str);
return tmp;
}
};
If I write a code like Foo f3; f3 = f1 + f2;, runtime error occurs when this->str = rhs.str; in operator=() is executed.
I think temporary std::string object(this->str + rhs.str) made in operator+() is regional so it is removed when the function call is finished. But int value(this->num + rhs.num) is passed well without problems.
Why is this difference happening?
You already correctly asserted that tmp is only local to Foo::operator+ and thus the returned reference is invalid (a dangling reference).
But int value(this->num + rhs.num) is passed well without problems.
I think you refer to code like this:
int operator+(const Foo& rhs) {
int tmp(this->num + rhs.num);
return tmp;
}
Yes, that works. Notice the important difference that int operator+(const Foo& rhs) returns a value. That is, you don't return a reference to tmp, but a copy of it.
That's why an idiomatic operator+ would return by value instead of by reference. You want:
// V no reference
Foo operator+(const Foo& rhs) { ... }
See also this great question about canonical operator overloading.
Your operator+ returns a reference to the temporary Foo called tmp. It will be automatically destroyed after the return.
You don't show a minimal, compilable example. But most likely, the assignment afterwards to f3 then fails.
The idiomatic approach for an operator+ is to return Foo by value.
// Note the missing & in the Foo return value
Foo operator+( const Foo& rhs )
{
Foo result( this->num + rhs.num, this->str + rhs.str );
return result;
}

Why I add an "&" ,it outputs different?

I've asked a similar question before (Why the code doesn't work on CodeBlocks,but on VS works).
Now a new error confused me.
cout << ++a1 << endl; will call the function operator double().
If in the code Fraction& operator++() I remove the & to make it Fraction operator++(), it will call the ostream& operator<<(ostream& os, Fraction&& obj).
#include <iostream>
using namespace std;
class Fraction
{
private:
int fenzi, fenmu;
public:
Fraction(int a, int b) :fenzi(a), fenmu(b) {}
operator double()
{
return 1.0* fenzi / fenmu;
}
friend ostream& operator<<(ostream& os, Fraction&& obj)
{
os << obj.fenzi << "/" << obj.fenmu;
return os;
}
Fraction& operator++()
{
fenzi++;
fenmu++;
return *this;
}
Fraction operator++(int)
{
Fraction tmp(fenzi, fenmu);
fenzi++;
fenmu++;
return tmp;
}
};
int main()
{
Fraction a1(9, 11), a2(1, 2);
cout << double(a2) << endl;
cout << ++a1 << endl;
cout << a1++ << endl;
return 0;
}
I wonder why is the output different?
Fraction operator++() and Fraction& operator++(), the former returns a copied one, and the latter returns the original one. But their types are both Fraction. I think it should have both called ostream& operator<<(ostream& os, Fraction&& obj).
Fraction operator++() output(I expected):
0.5
10/12
10/12
Fraction& operator++() output:
0.5
0.833333
10/12
operator double()
{
return 1.0* fenzi / fenmu;
}
Is the implicit conversion operator. Adding the explicit keyword to the operator double() will help with this because the compiler won't implicitly convert a Fraction into a double without you explicitly casting it into a double. The addition would look like:
explicit operator double()
{
return 1.0* fenzi / fenmu;
}
The correct output stream operator is defined as
friend ostream& operator<<(ostream& os, Fraction& obj). You defined it as friend ostream& operator<<(ostream& os, Fraction&& obj)
Fraction&& is a rvalue reference instead of a lvalue reference (Fraction&). The compiler used the implicit conversion operator because the operator++ (in either definition) returns a lvalue (reference) instead of a rvalue reference which was defined as the parameter of the output stream operator.

What's the difference returning & and not?

I still haven't figured out how the & works in the compiler.
To figure it out, I tried disassembling the code below:
private:
int data;
public:
int& at()
{
return data;
}
int at()
{
return data;
}
const int& result = mymay.at(); //call the two functions
I find that int& at() returns an address, and int at() returns a value; the compiler first writes the value to a memory, then "result"'s address was set to it.
So I understand that int at() will return a copied one.
I also understand that it's best practice to write friend ostream& operator<<(ostream &os , const A &obj)
But I wonder if it's correct: in the below code, A &get1() returns an l-value, and A get2() returns an r-value.
#include <iostream>
using namespace std;
class A
{
public:
A &get1(){
return *this;
}
A get2(){
return *this;
}
friend ostream& operator<<(ostream &os , A &obj){
cout<<"An l-value function called."<<endl;
return os;
}
friend ostream& operator<<(ostream &os , A &&obj){
cout<<"An r-value function called."<<endl;
return os;
}
};
int main()
{
A tmp;
cout<<"get1: "<<tmp.get1()<<"get2: "<<tmp.get2()<<endl;
return 0;
}
Actual results:
An l-value function called.
An r-value function called.
The version of at that returns a reference allows you to modify the member datum data via that reference:
mymay.at() = 1;
will set data in the instance of mymay to 1. So mymay.at() is an l-value: i.e. it can be placed on the left hand side of an assignment.
The version of at that returns a value will not allow you to do that. It's not an l-value.
Your second point, an l-value cannot bind to an rvalue reference && wheres an anonymous temporary can. Overload resolution for your cout is unambiguous which accounts for the output of your program.

why do we have to return const reference from unary prefix operator overloading

I have one doubt on prefix operator overloading .
my sample program:
class ABC {
int i;
public:
const ABC& operator++() { i=i+1; return *this;}
};
int main() {
ABC ob ; //let value of i =5;
++ob; // value of i will be 6
return 0;
}
but I could do the same thing by overloading like below
void operator++() { i=i+1;}
this gives me same result when calling ++ob
APMK ++ob converted as ob.operator++().
My doubt is what happens to the return value. Does the compiler creates code like:
ob = ob.operator++();
Thanks in advance
why do we have to return const reference from unary prefix operator overloading
You don't have to. Typically you return non-const reference, but you could make it return anything. You shouldn't, but you could. The built in pre-increment works how returning non-const reference works, so it makes the most sense to do that.
The reason to return a reference to an object is to make the behavior the same as for integers (for built in types).
std::cout << ++i;
std::cout << i++;
If i is an integer this will work. This will only work in a class if you return a reference to the object (Assuming you have defined operator<< for your class).
class ABC {
int i;
public:
ABC(int x = 0) : i(x) {}
const ABC& operator++() { i=i+1; return *this;}
ABC operator++(int) { ABC result(*this);i=i+1; return result;}
friend std::ostream& operator<<(std::ostream& out, ABC const& val)
{
return out << val.i;
}
};
With this definition then your class will behave the same way that an integer will behave in the above situation. Which makes it very useful when using template as you can now do a drop in replacement for integer with your type.
Note normally I would return just a reference (not a const reference).
/*const*/ ABC& operator++() { i=i+1; return *this;}
^^^^^^^^^
see: https://stackoverflow.com/a/3846374/14065
But C++ allows you do anything and you can return whatever is useful or has appropriate meaning for the situation. You should consider what is meaningful for your class and return a value that is appropriate. Remember the Principle of lease surprise

Error while overloading increment operator inside overloaded I/O operator

I am a beginner to OOPS concept. I am working on Operator overloading now. I run into the error no match for operator<<when I use an overloaded increment operator inside cout. It works fine when I remove the overloaded increment from cout. Logically, I don't feel like anything wrong with the code. Still, I do not know why I get the error? Below is my code for better understanding of the issue.
#include <iostream>
using namespace std;
class Digit
{
private:
int digit;
public:
Digit(int n)
{
digit = n;
}
Digit& operator++();
Digit operator++(int); //overloaded postfix increment. Dummy argument used
Digit& operator--();
Digit operator--(int); //overloaded postfix decrement. Dummy argument used
friend ostream& operator<<(ostream& out, Digit& x); //overloaded << prototype
int GetDigit()
{
return digit;
}
};
Digit Digit::operator++(int)
{
//Create a temporary object with a variable
Digit temp(digit);
//Use prefix operator to increment this Digit
++(*this);
return temp; //return temporary result
}
Digit& Digit::operator++()
{
if (digit==9)
digit = 0;
else
++digit;
return *this;
}
Digit Digit::operator--(int)
{
//Create a temporary object with a variable
Digit temp(digit);
//Use prefix operator to increment this Digit
--(*this);
return temp; //return temporary result
}
Digit& Digit::operator--()
{
if (digit==0)
digit = 9;
else
--digit;
return *this;
}
int main()
{
using namespace std;
Digit n(9);
Digit x(0);
cout << n++ << endl;
cout << x-- << endl;
return 0;
}
ostream& operator<<(ostream& out, Digit& x)
{
out << x.digit;
return out;
}
The lines cout << n++ << endl; cout << x-- << endl;inside main() causes the error.
It's because the postfix operators return by value, and if you don't save that value you will have a temporary, and non-const references can't bind to temporaries.
Simple fix is to have your output operator take the Digit argument by const reference:
ostream& operator<<(ostream& out, Digit const& x)
// ^^^^^