Madryoch
Madryoch

Reputation: 85

operator<< overload. Unable to directly use the result of another overloaded operator directly in cout

I am making a Complex number class in order to work on overloading operators.

#include <iostream>
class Complex
{
    double real;
    double imaginary;

public:

    Complex(double real, double imaginary) : real(real), imaginary(imaginary) {}
    ~Complex() = default;

    friend std::ostream &operator<<(std::ostream &out, Complex &source)
    {
        out << "(" << source.real << " + " << source.imaginary << ")";
        return out;
    }

    friend Complex operator+(const Complex &a, const Complex &b)
    {
        return Complex(a.real + b.real, a.imaginary + b.imaginary);
    }

};

int main()
{
    Complex c1(3, 2.25);
    Complex c2(2.25, 3);
    Complex res = c1 + c2;

    std::cout << res;
    return 0;
}

The class definition is not finished as I need to overload a few operators more. However if I compile and run the project I get the result printed on my screen as expected though if I don't use a result variable in order to print for cout cout<< c1+c2;I am getting the following error:

error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'cmp::Complex')

If I try to use cout<< &(c1+c2); I get the error message:

error: taking address of temporary [-fpermissive]

and it was not my intention to have to write it like that.

I am under the impression that it fails because c1+c2 is not taken as a reference since it is a temporary object that is not saved anywhere and since I cannot take a reference of a temporary object according to the second error, it fails. This explains why when I save the result of c1+c2 on result I can execute the program without errors.

In the video I was watching, Eclipse was used and in my case I am using Codeblocks with GNU GCC compiler.

Could you help me understand what am I doing wrong ? Why isn't it working in my case but works with the same syntax on the video ?

EDIT: Solution: The << operator function should be taking a const reference of Complex type instead. A temporary object can only be bound to a const reference.Thus the prototype of it should look something like this...

friend ostream &operator<<(ostream &out,const Complex &source);

Upvotes: 0

Views: 64

Answers (2)

Smit Ycyken
Smit Ycyken

Reputation: 1181

Problem:

...
friend ostream &operator<<(ostream &out,Complex &source);
...
cout<< c1+c2; // error or warning
...

Why isn't it working?

Expression of c1+c2evaluates to temporary object of prvalue.

std::cout<<c1+c2; is trying to bind prvalue to lvalue reference

All temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created, and if multiple temporary objects were created, they are destroyed in the order opposite to the order of creation. This is true even if that evaluation ends in throwing an exception.

Solution?

The lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference (since C++11)

...
//  using rvalue reference
friend std::ostream &operator<<(std::ostream &out, Complex &&source)
{
    source.real += 100; // non const value in function signature allows us to modify 'source'
    out << "(" << source.real << " + " << source.imaginary << ")";
    return out;
}
...

Adding const qualifier to the function argument:

...
// using const lvalue reference
friend std::ostream &operator<<(std::ostream &out, const Complex &source)
{
    out << "(" << source.real << " + " << source.imaginary << ")";
    return out;
}
...

What if we have all of this overloads at the same time?

When used as a function argument and when two overloads of the function are available, one taking rvalue reference parameter and the other taking lvalue reference to const parameter, an rvalue binds to the rvalue reference overload (thus, if both copy and move constructors are available, an rvalue argument invokes the move constructor, and likewise with copy and move assignment operators).

Example:

#include ...

int main()
{
    Complex c1(3, 2.25);
    Complex c2(2.25, 3);

    auto lvalue = c1 + c2;
    auto && rlvalueRef = c1 + c2;
    const auto& constlvalueRef = c1 + c2;
    const auto constlvalue = c1 + c2;

    std::cout
        << constlvalue
        << lvalue
        << rlvalueRef
        << constlvalueRef
        << c1 + c2
        << std::endl;

    return 0;
}

Output:

non modified: (5.25 + 5.25)
non modified: (5.25 + 5.25)
non modified: (5.25 + 5.25)
non modified: (5.25 + 5.25)
modified output: (105.25 + 5.25)

Upvotes: 1

Alan Birtles
Alan Birtles

Reputation: 36399

c1+c2 produces a temporary object, this can't be bound to the non-const reference in your stream operator. You need to change it to a const reference

Upvotes: 2

Related Questions