cannot bind non-const lvalue reference of type 'Fraction&' to an rvalue of type 'Fraction'

77 Views Asked by At

I am trying to make a struct Fraction. I have defined constructors and operator>> and operator<< for it, and that works correctly.

When I was defining operator+ I got the following error message with my code:

cannot bind non-const lvalue reference of type 'Fraction&' to an rvalue of type 'Fraction'

This is my code:

struct Fraction
{
    int a;
    int b;

    Fraction()
    {
        a = 0;
        b = 1;
    }

    Fraction(Fraction& f)
    {
        a = f.a;
        b = f.b;
    }

    Fraction(int x, int y)
    {
        a = x;
        b = y;
        if (b < 0)
        {
            b *= -1;
            a *= -1;
        }
    }
};

istream & operator>>(istream & in, Fraction & f)
{
    ...
    return in;
}

ostream & operator<<(ostream & out, Fraction f)
{
    if (f.b == 1 || f.a == 0)
        out << f.a;
    else
        out << f.a << "/" << f.b;
    return out;
}

Fraction operator+(Fraction f1, Fraction f2)
{
    Fraction f((f1.a * f2.b) + (f2.a * f1.b), f1.b * f2.b);
    return f;
}

int main()
{
   Fraction a, b;

   cin >> a >> b;
   cout << a + b << endl;
}

If I change the last part and write this, it works correctly:

int main()
{
   Fraction a, b, c;

   cin >> a >> b;
   c = a + b;
   cout << c << endl;
}

I don't understand why this happens at all, and I would like to figure it out.

I'm just a beginner and I understand that the question may be stupid and obvious. Could you please explain what lvalue and rvalue are, how to use them, and how to correct the code? I have already read some articles about this, but I still don't understand how this all relates to my situation.

1

There are 1 best solutions below

0
Remy Lebeau On

An lvalue (historically, something that appears on the left-side of an assignment) generally refers to anything that has a name.

An rvalue (historically, something that appears on the right-hand side of an assignment) generally refers to anything that is temporary.

See Value Categories on cppreference.com for more specific details.

A reference to a non-const object requires a pre-existing object to bind to, it cannot bind to a temporary object. That is what the compiler error is complaining about. A reference to a const object, on the other hand, can bind to a temporary object.

In your first example, the expression cout << a + b returns a temporary object from operator+ and then passes it as-is to your operator<<. Your operator<< is taking in a Fraction object by value for its f parameter, so anything that is passed in to it requires the compiler to create a new Fraction object for f. Since you are passing in a (temporary) Fraction object as input, the Fraction copy constructor gets called, but your copy constructor does not accept a temporary object as input, hence the compiler error.

In your second example, you are assigning the temporary Fraction object to a local variable first, and then the Fraction copy constructor is able to bind its f parameter to that object when the compiler is constructing the f parameter of operator<<.

To fix this, you need to either:

  • omit your copy constructor altogether and let the compiler generate a default copy constructor for you (just as you are doing for the default compiler-generated assignment operator=). The compiler will produce the correct code in this example.

  • change your copy constructor to take in a reference to a const object instead, eg:

    Fraction(const Fraction & f)
    {
        a = f.a;
        b = f.b;
    }
    

Either way, your operator<< and operator+ should take in their Fraction inputs as const references, eg:

ostream& operator<<(ostream & out, const Fraction & f)
{
    if (f.b == 1 || f.a == 0)
        out << f.a;
    else
        out << f.a << "/" << f.b;
    return out;
}

Fraction operator+(const Fraction & f1, const Fraction & f2)
{
    Fraction f((f1.a * f2.b) + (f2.a * f1.b), f1.b * f2.b);
    return f;
}