V
V
Vrnv2020-03-19 18:28:04
OOP
Vrnv, 2020-03-19 18:28:04

How to correctly overload constructors in a class?

I ran into a problem: With such an entry as below, everything works and everything compiles fine:

#include <iostream>
using namespace std;


class A{
    private: 
        int aa;
        int ab;
    public:
        A():aa(0),ab(0){}
        A(int a):aa(a),ab(0){}
        void Show(){
            cout<<"Var1: "<<aa<<endl<<"Var2: "<<ab<<endl;
        }
};


int main(){
    A obj = 5;
    obj.Show();



    return 0;
}

But if I add a copy constructor, then I get an error:
cannot bind non-const lvalue reference of type 'A&' to an rvalue of type 'A'
     A obj = 5;


#include <iostream>
using namespace std;


class A{
    private: 
        int aa;
        int ab;
    public:
        A():aa(0),ab(0){}
        A(int a):aa(a),ab(0){}
        A(A& obj);
        void Show(){
            cout<<"Var1: "<<aa<<endl<<"Var2: "<<ab<<endl;
        }
};


int main(){
    A obj = 5;
    obj.Show();



    return 0;
}

Prompt: How it is correct to overload constructors so that there was no this conflict?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
V
Victor Bomberow, 2020-03-19
@Vrnv

Let's consider the problem in more detail.

class A {
private:
 int aa;
 int ab;

public:
 A()
     : aa(0), ab(0) {}  // (4) note: candidate constructor not viable: requires
                        // 0 arguments, but 1 was provided
 A(int a)
     : aa(a), ab(0) {}  // (2) note: candidate constructor not viable: no known
                        // conversion from 'A' to 'int' for 1st argument
 A(A& obj);  // (3) note: andidate constructor not viable: expects an l-value
             // for 1st argument
 void Show() { cout << "Var1: " << aa << endl << "Var2: " << ab << endl; }
};

int main() {
 A obj = 5;  // (1) error: cannot bind non-const lvalue reference of type ‘A&’
             // to an rvalue of type ‘A’
 obj.Show();

 return 0;
}

The standard obliges to understand
A(A& obj);
as a user-defined copy constructor. Therefore, the copy constructor will not be declared by default by the compiler. From the C++11 standard, it is possible to force the compiler to generate an implicitly-declared copy constructor with the keyword default . The default implicitly-declared copy constructor has a signature, but only if all base classes T have copy constructors with parameters const B& or const volatile B&, and when all non-static data members of class T have copy constructors with const M& or const volatile M& parameters. Otherwise, the implicitly declared copy constructor has the signatureT::T(const T&)
A(A& obj) = default;
T::T(T&)
In this case, the copy constructor A(A& obj) is trivial with the signature of the generated implicitly defined copy constructor T::T(T&).
Trivial copying is almost the same as std::memmove
The following happens on the compile-time error line:
1) the = operator on the compile-time error line is not initializing, because the literal 5 is (rvalue, address cannot be taken) const int. To perform an assignment operation, the compiler first constructs A(5);
...
A(int a) : aa(a), ab(0) {
 cout << "Copy ctor with 1 parameter is called " << endl;
}
…
int main() {
…

Вывод:
Copy ctor with 1 parameter is called
Var1: 5
Var2: 0

2) the assignment operator deals with A obj = A (5):
To the right of the assignment operator is a temporary rvalue of type class A.
This assignment is initializing, which makes it equivalent to A obj(A(5));
This operation requires a constructor with signature T::T(T&&)
This is a move constructor, and it is not present in class A, since the implicit definition of a move constructor in the class requires the absence of user-defined copy constructors, copy = operator, assignment = operator, destructor . In our case, we have user-defined A(A& obj);
Given the above, to fix the compilation error, you can either remove the line
A(A& obj); ,
which will lead to the compiler implicitly defining trivial copy and move constructors, or add a line to the class A declaration.
A(A&& obj);
From the point of view of the C++11 standard and above, it can be argued that the expressions A(A& obj); and A(A&&obj); are respectively equivalent to A(A& obj) = default; and A(A&& obj) = default;

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question