0

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).

Davide Spataro
  • 7,319
  • 1
  • 24
  • 36
Gil
  • 143
  • 2
  • 9

1 Answers1

7

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.

Davide Spataro
  • 7,319
  • 1
  • 24
  • 36
  • what's the difference between `A&& xxx = 1; ` and `A xxxx = 1;`? – dragonxlwang Mar 21 '19 at 01:11
  • @dragonxlwang The first is a reference to a `rvalue` (the temporary and anonymous `1` on the right of the `=` sign) while the second is a *normal* variable to which `1` gets copied in. – Davide Spataro Mar 21 '19 at 09:08