Home > OS >  The functionality of const data_type& in C
The functionality of const data_type& in C

Time:11-21

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?

CodePudding user response:

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.

  •  Tags:  
  • c
  • Related