C++ Question

Why copy constructor needs to be const?

I have a question about copy constructors, suppose a

class A
that contains an
int
, with default constructor, copy constructor (?), and constructor that receives an
int


class A {
int value;
public:
A(): value(0) {} // Default
A(A& copy): value(copy.value) {} // Copy (?)
A(const int n): value(n) {} // Receives an int for 'value'

int get_value() { return value; } // Return value
};


And a class that contains an
A pointer
, named
BoxForA
:

class BoxForA {
A *obj;
public:
BoxForA(): obj(nullptr) {}
BoxForA(const int a) { obj = new A(a); }

A popf(){ return *obj; } // Returns the content of obj_A (an A element)
};


And the main (that will print three
3
):

int main() {
A a(3);
A b = a; // Calling copy constructor for b (this works with non-const parameter)

BoxForA box1(3);
A c = box1.popf(); // Calling copy constructor for c (this doesn't work with non-const parameter)

cout << "a = " << a.get_value() << endl;
cout << "b = " << b.get_value() << endl;
cout << "c = " << c.get_value() << endl;
return 0;
}


When I compiled this, the compiler gave me this error:

error: invalid initialization of non-const reference of type ‘A&’ from an rvalue of type ‘A’
A c = box1.popf();
^


Just with that, I suppose that my copy constructor need const parameter, instead of non-const, so editing my copy constructor like this:

A(const A& obj): value(obj.value) {}


My program now compiles without errors and show this output:

a = 3
b = 3
c = 3


Nice! I found the solution :D .... Nope, because I don't know why it works.

Can you tell me why happens that? Why the copy constructor seems to have
const
parameter?

Answer

When you write

A c = box1.popf();

It calls the copy constructor, as you know:

A c(box1.popf()); // copy constructor

Now, what does popf return? It returns an A, a prvalue (basically an rvalue - there is a difference in C++11). But wait, the copy constructor takes a &, which only binds to lvalues, and so your code doesn't compile.

But when you add const, the signature is now const&, which accepts rvalues and lvalues, and the code compiles.

You get exactly the same problem if you write:

int& a = 1; // 1 is a prvalue, but a only accepts lvalues
Comments