-1

I'm trying to learn C++ with the classical Point example and got my g++ confused about the constructor.

All is fine with this code:

class Point {
    int x, y;
public:
    Point(int a=0, int b=0) {x=a; y=b;}
    //    Point(Point &p) {x=p.x; y=p.y;}
    //    Point(const Point &p) {x=p.x; y=p.y;}
};

int main()
{
    Point p = Point(1, 2);
    return 0;
}

But with two constructors, g++ can't compile:

class Point {
    int x, y;
public:
    Point(int a=0, int b=0) {x=a; y=b;}
    Point(Point &p) {x=p.x; y=p.y;}
    //    Point(const Point &p) {x=p.x; y=p.y;}
};

int main()
{
    Point p = Point(1, 2);
    return 0;
}

The error message is:

cpp2.cpp:10: error: no matching function for call to `Point::Point(Point)'
cpp2.cpp:5: note: candidates are: Point::Point(Point&)
cpp2.cpp:4: note:                 Point::Point(int, int)

And with three constructors, it compiles ok :

class Point {
    int x, y;
public:
    Point(int a=0, int b=0) {x=a; y=b;}
    Point(Point &p) {x=p.x; y=p.y;}
    Point(const Point &p) {x=p.x; y=p.y;}
};

int main()
{
    Point p = Point(1, 2);
    return 0;
}

I supposed the first constructor was ok, no matter the presence of one or two more constructors. Could somebody explain me my mistake ?

pdemal
  • 11
  • 2
  • Style note: use initializer lists in your constructors: E.g. `Point(int a=0, int b=0) : x(a), y(b) { }` – Chris Apr 01 '23 at 06:15
  • You've been given good explanations below, but sometimes C++ can be learned by just using best practise until your understanding has increased and you can follow the technical stuff. In this case the best practise is that when you pass a parameter by reference (i.e. `(Point& p)`) consider whether you are going to use that reference to modify the target of the reference. If not (as in the case of a copy constructor) then add a `const` to the reference (i.e. `(const Point& p)`). Follow that rule and you won't go far wrong. – john Apr 01 '23 at 06:24

2 Answers2

0

The problem is that with the definition

Point p = Point(1, 2);

the result of Point(1, 2) is a temporary object, an rvalue. Non-constant references (like Point&) can't be bound to temporary objects.

That's why the constructor Point(const Point&) works, it can be passed any Point object even if it's temporary.

It's also that constructor which is considered the "proper" copy-constructor.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Please tell me more: why does my code compile ok when only the first constructor is defined: Point(int a=0, int b=0), and refuses to compile when adding an irrelevant constructor? – pdemal Apr 01 '23 at 06:31
  • @pdemal That's because then the compiler will create a (proper) copy-constructor for you. – Some programmer dude Apr 01 '23 at 06:32
  • 2
    Ok, I got it: with only the first constructor defined, the compiler creates its own copy-constructor, but when the second constructor is defined, which is an irrelevant copy-constructor, it can't and issues an error. – pdemal Apr 01 '23 at 06:54
0

In the case that didn't compile, your copy constructor only accepts left values (non-temporary object). However, in your main function, you passed in Point(1, 2), which is an rvalue (temporary object). You can try to modify the main function of your failed case to this:

int main()
{
    Point a(1, 2);
    Point p = a;
    return 0;
}

Then it should compile and run. For rvalue and lvalue, you might find some useful resources online.