Gil
Gil

Reputation: 157

Binding rvalue to lvalue reference

I have the following c++ code (VS2013):

#include <iostream>
using namespace std;

class A {
    int i;
public:
    A(int i) : i(i) {
        cout << "Constructor: " << i << endl;
    }
    A(const A &o) : i(o.i) {
        cout << "Copy constructor: " << i << endl;
    }
    ~A() {
        cout << "Destructor: " << i << endl;
    }
};

A test(const A &a, A b, A *c) {
    return *c;
}

int main() {
    A b(10);
    cout << "START OF TEST" << endl;
    test(1, b, &b);
    cout << "END OF TEST" << endl;
    system("pause");
}

When running the code, I get the following output between the "START OF TEST" and "END OF TEST" outputs:

Constructor: 1

Copy constructor: 10

Copy constructor: 10

Destructor: 10

Destructor: 10

Destructor: 1

3 objects are built: 1 using an integer 1, and 2 using an object of class A (with i = 10).

It's worth mentioning that when the test function's argument const A &a is changed to A &a (not a constant), the program does not compile, giving the following error:

Error C2664: 'A test(A &,A,A *)' : cannot convert argument 1 from 'int' to 'A &'

How is this behavior explained?

Specifically:

  1. Why does sending an integer 1 to test make A's parameter constructor A(int i) work (and only when const is used)?

  2. Why does A's copy constructor A(const A &o) work twice? (one run occurs when calling test, and another when returning *c).

Upvotes: 0

Views: 4189

Answers (1)

Davide Spataro
Davide Spataro

Reputation: 7482

Well, calling test with the first argument 1 causes the creation of a rvalue of type A. An rvalue can be assigned to a const lvalue reference but not to a plain lvalue reference. If you want it to compile without using const you have to specify that the parameter is a rvalue reference.

g++ error is a little bit more informative:

 error: cannot bind non-const lvalue reference of type ‘A&’ to an rvalue of type ‘A’
     test(A(1), b, &b);

rvalue can be assigned to an rvalue reference or to a lvalue reference to const.

  • Why is that? rvalues are temporary objects or literals. If this code was legal

    int &r = 5

    then you would be able to modify 5. On the other hand lvalue references to const forbids any change to the object they reference and thus you may bind them to a rvalue.


const A& x = 1; //compile
x = 2;         //error!
A&& xxx = 1; //compile
A& xx  = 1; //does not compile.

Regarding the second question. You are returning a copy of A from test so *c triggers the construction of a copy of c. Try returning a reference A from test to see that the constructor is not called.

Upvotes: 7

Related Questions