0

when working with operator <<(for standard output) in C++, how comes that the parameter const data_type& object can work with VALUES AND OBJECTS, but if i do data_type& object or data_type&& object it doesnt work? If lets say i have Complex Class, and i have defined operator << and operator + for addition of two complex numbers and my functions for these operator are declared as:

friend ostream& operator<<(ostream& out, Complex& c);
Complex operator+(Complex&c2);

and i do something like

cout << c1 * c2; //doesnt work
cout << c1; cout << c2; //it works

but when I put in operator<< const Complex& c, both of these work, why is that?

Mat
  • 202,337
  • 40
  • 393
  • 406
  • What is the difference between a value and an object for you? – Ulrich Eckhardt Nov 20 '21 at 11:36
  • 3
    Assuming you have a suitable `operator*` defined for your class, then the result of e.g. `c1 * c2` is an rvalue (a temporary object). And rvalues/temporary objects can't be bounds to non-const references. Please invest in [some good books](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282) as they should have explanations of such things and about [the value categories](https://en.cppreference.com/w/cpp/language/value_category). – Some programmer dude Nov 20 '21 at 11:40

1 Answers1

0

The fundamental reason of why/how this works is operator precedence. In the below example i have explained step by step how the program works. At the end of the program i have also explained why in your case/example it doesn't work while in my case it does.


#include <iostream>

class Complex 
{
   //friend declaration for overloading different operators
   friend std::ostream& operator<<(std::ostream& os, const Complex&);//note that the second argument is "reference to const"
   friend Complex operator*(const Complex& obj1, const Complex& obj2);
   
    public:
    //lets overload operator+ as member function just for example in this case
    Complex operator+(const Complex& obj2)
    {
        std::cout << "Overloaded operator+ called"<<std::endl;
        return Complex();
    }
};
//note that the second argument is a reference to const
std::ostream& operator<<(std::ostream& os, const Complex& obj)
{
    std::cout<<"Overloaded operator<< called"<<std::endl; 
    return os;
}
Complex operator*(const Complex& obj1, const Complex& obj2)
{
    std::cout<<"Overloaded operator* called"<<std::endl;
    return Complex();
}

int main()
{
    //create Complex object
    Complex c1, c2;
    
    std::cout << c1; //this uses overloaded operator<<
    
    Complex c3;
    c3 = c1 + c2; //this uses overloaded operator+
    /*Explanation of c3 = c1 + c2;
    Here due to operator precedence objects c1 and c2 are grouped together. That is, since operator+ has higher precedence that operator= 
    c1 and c2 are grouped together. So the above is equivalent to writing c3 = (c1 + c2);
    Next assignment operator= is used to assign the value of (c1 + c2) to c3.
    
    
    */
    
    c3 = c1 * c2; //this uses overloaded operator*
    /*Explanation of c3 = c1 * c2;
    Since operator* has higher precedence than operator= the objects c1 and c2 are grouped together and the above statement is equivalent to 
    writing c3 = (c1 * c2);
    Next assignment operator= is used just like in the last case.
    */
    
    //now lets take a look at your question statment
    std::cout << c1 * c2 ; //this works because the second argument is a reference to const in my example(unlike yours)
    /*Explanation of std::cout << c1 * c2;
    Now again, operator precedence is the fundamental reason here. Since operator* has higher precednece than operator<< the objects c1 and c2
    are grouped together and so the above essentially means std::cout << (c1 * c2);
    Note that our overloaded operator* returns a Complex object by value.
    Next since the result that was returned in the last step is a "temporary" Complex object but we have overloaded operator<< with its 
    second argument as reference to const, this works. The important thing to note is that this works in my example because a reference to const
    can be bound to a temporary object. But it doesn't work in your case(example) because in your case the overloaded operator<< has its second argument
    as reference to nonconst and we can only bind a reference to a nonconst to an lvalue.
    */
    
    
    return 0;
}

The output of the above program can be seen here.

Let's see in step by step manner for my above given example what is going on:

Case I: For std::cout << c1;

Since we have overloaded operator<< it will be used.

Case II: For c3 = c1 + c2;

Here due to operator precedence objects c1 and c2 are grouped together. That is, since operator+ has higher precedence that operator= objects c1 and c2 are grouped together. So the above is equivalent to writing c3 = (c1 + c2);

Next assignment operator= is used to assign the value of (c1 + c2) to c3.

Case III: For c3 = c1 * c2;

Since operator* has higher precedence than operator= the objects c1 and c2 are grouped together and the above statement is equivalent to writing c3 = (c1 * c2);

Next assignment operator= is used to assign the value of (c1 * c2) to c3.

Now next case is regarding your question, look at it carefully.

Case IV: For std::cout << c1 * c2 ;

Now again, operator precedence is the fundamental reason here. Since operator* has higher precedence than operator<< the objects c1 and c2 are grouped together and so the above essentially means std::cout << (c1 * c2);

Note that our overloaded operator* returns a Complex object by value.

Next since the result that was returned in the last step is a "temporary" Complex object but we have overloaded operator<< with its second argument as reference to const, this works since a reference to const can be bind to a temporary object.

The important thing to note is that this works in my example because a reference to const can be bound to a temporary object. But it doesn't work in your case(example) because in your case the overloaded operator<< has its second argument as reference to nonconst and we can only bind a reference to a nonconst to an lvalue.

Jason
  • 36,170
  • 5
  • 26
  • 60