Reputation: 71
Consider the following example:
#include<iostream>
using namespace std;
class Point
{
private:
int x, y;
public:
Point(int x1, int y1) { x = x1; y = y1; }
// Copy constructor
Point(const Point& p2) { x = p2.x; y = p2.y; }
int getX() { return x; }
int getY() { return y; }
};
int main()
{
Point p1(10, 15);
Point p2 = p1;
cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();
cout << "\np2.x = " << p2.getX() << ", p2.y = " << p2.getY();
return 0;
}
We notice that Point(const Point& p2);
is a copy constructor. Why do we use the const
keyword, and why do we use a reference? Why don't we use Point(Point p2);
?
Upvotes: 0
Views: 394
Reputation: 73186
Why do we use the const keyword [...] ?
You do not need to use a const
-qualified (or even cv-qualified) argument to a class copy constructor; citing cppreference / Copy Constructors:
A copy constructor of class
T
is a non-template constructor whose first parameter isT&
,const T&
,volatile T&
, orconst volatile T&
, and either there are no other parameters, or the rest of the parameters all have default values.
but it would be quite unusual for a copy constructor to need to mutate the object it uses for the copy-in.
#include <iostream>
struct A {
int x;
A(int x_) : x(x_) {}
A(A& other) : x(other.x) { ++other.x; }
};
int main() {
A a1{42}; // a1.x is 42.
A a2(a1); // a2.x is 42, AND a1.x is 43
std::cout << a1.x << " " << a2.x; // 43 42
}
and, moreover, a non-const
reference cannot bind to rvalues to extend their lifetime, meaning such a copy constructor could not be used to copy-initialize from a temporary object. I.e., the following (common case) is well-formed:
struct A {
int x;
A(int x_) : x(x_) {}
A(const A& other) : x(other.x) { }
static A create() { return {12}; };
};
int main() {
A a1(A::create()); // OK.
}
whereas the following is ill-formed (prior to guaranteed copy elision in C++17):
struct A {
int x;
A(int x_) : x(x_) {}
A(A& other) : x(other.x) { }
//^^ non-const ref cannot bind to rvalue.
static A create() { return {12}; };
};
int main() {
// Ill-formed before C++17 and guaranteed copy elision.
A a1(A::create()); // (C++14) error: no matching constructor for initialization of 'A'
}
[...] and why do we use a reference?
If the language were to actually allow declaring a copy constructor as taking its argument (say, the copy-from object) by value instead of reference, without any other changes to the language, simply passing copy-from object to the copy constructor (before reaching the implementation of it!) would require copying the object from the call site. Thus, such a copy constructor would require copying an object prior to defining how to copy the object, a recursive logic that is naturally not sound.
Upvotes: 5