Confusion about Copy Constructor in C++ - c++

Code:
class A
{
public:
A()
{
cout<<"Defualt Constructor"<<endl;
}
A(A &t)
{
cout<<"Copy Constructor"<<endl;
}
};
A func()
{
cout<<"In func"<<endl;
}
int main()
{
A a1;
A a2;
a2 = func();
return 0;
}
The program works fine. Also, If I call function like this:
A a2 = func();
and add const qualifier in copy constructor argument like:
A(const A &t)
{
cout<<"Copy Constructor"<<endl;
}
Also, working fine.
But, If remove const from copy constructor argument like:
A(A &t)
{
cout<<"Copy Constructor"<<endl;
}
and call function func()
A a2 = func();
Compiler giving an error:
error: invalid initialization of non-const reference of type 'A&' from an rvalue of type 'A'
A a2 = func();
^
prog.cpp:13:9: note: initializing argument 1 of 'A::A(A&)'
A(A &t)
^
Why does compiler give an error in last case?

A a2 = func(); is copy initialization, a2 will be initialized from the object returned by func() via copy constructor. func() returns by value, so what it returns is a temporary, which can't be bound to lvalue-reference to non-const (i.e. A &), that's why you get the error.
Temporary could be bound to lvalue-reference to const (or rvalue-reference), so changing the parameter type to const A &t (or adding move constructor) would make it work fine.
BTW: a2 = func(); has nothing to do with copy constructor, but copy assignment operator. You didn't declare it for A, and the implicitly declared copy assignment operator takes const A& as parameter, then it's fine.
BTW2: func() returns nothing. Note that flowing off the end of a non-void function without returning leads to UB.

Related

move constructor when returning a "chained" object

Say I have a class that is move-only, and this class has methods that "chain". example:
struct C {
C() = default;
C(C const&) = delete;
C(C&&) = default;
C& chained() {
return *this;
}
int a;
};
C foo(C c) {
return c.chained();
}
int main()
{
auto o = foo(C{});
}
I get an error at the return statement of foo: "Use of deleted function 'C::C(const C&)'".
Why is trying to call the copy constructor? Shouldn't it be using the move constructor since its a return statement?
Why is trying to call the copy constructor? Shouldn't it be using the move constructor since its a return statement?
No. You are referring to, and misinterpreting, copy elision (as pertaining to NRVO). Only when the returned expression is a an id-expression (just a name) that refers to an object from the function's parameter list or a local, will a move be attempted first.
You don't return an id-expression, your expression is the result of a call to a member function.

Why does the compiler choose the copy ctor after calling move

For the following code, I know that because I implement a copy constructor the default move is blocked by the compiler, what I don't understand is why the compiler choose the copy constructor, and not issuing an error?
Can a rvalue reference be the parameter for a reference function if no rvalue ref function exists?
If not, what do happens here?
#include <iostream>
using namespace std;
class A{
public:
A(){printf("constructor\n");}
A(const A& a){printf("copy constructor");}
};
int main()
{
A a;
A b = std::move(a);
return 1;
}
The output is:
constructor
copy constructor
Thanks
The reason is that rvalue expressions can be bound to function parameters that are const lvalue references.
Also note that const rvalue expressions cannot be bound to rvalue references:
class A{
public:
A(){printf("constructor\n");}
A(const A& a){printf("copy constructor");}
A(A&&) = default; // <-- has move ctor
};
int main()
{
const A a; // <-- const!
A b = std::move(a); // <-- copy not move!!!
return 1;
}
The code above still calls the copy constructor, even though there is a move constructor.
std::move will take your named value and turn it into an rvalue expression, nothing more.
And, just like for any other rvalue, if it can't be moved, it'll be copied.
An rvalue of type T can be bound to a const T& just fine.

class constructor matching, why didn't the move constructor called here?

I'm recently learning about move constructor, and I met a strange problem.
I have the following code:
#include <iostream>
class a
{
public:
a() { printf("default\n"); }
a(const a& aa) { printf("const lr\n"); }
a(a& aa) { printf("lr\n"); }
a(a&& aa) { printf("rr\n"); }
a(const a&& aa) { printf("const rr\n"); }
};
a foo()
{
return a();
}
void func(a& a) {
printf("func lr\n");
}
void func(a&& aa) {
printf("func rr\n");
}
int main()
{
printf("a1: ");a a1;
printf("a2: ");a a2(a1);
printf("a3: ");a a3(std::move(a2));
printf("a4: ");a a4(foo());
printf("a5: ");a a5(std::move(foo()));
func(foo());
}
The output is:
a1: default
a2: lr
a3: rr
a4: default
a5: default
rr
default
func rr
All is good except for a4. I expect that the return type of foo() is a rvalue. I think the call of func() at last has proved that. So why doesn't the move constructor called when constructing a4? And at the same time, it is called when constructing a5.
That's probably because of copy_elision.
Under the following circumstances, the compilers are required to omit
the copy- and move- construction of class object:
In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
In a function call, if the operand of a return statement is a prvalue and the return type of the function is the same as the type of that prvalue:
T f() { return T{}; }
T x = f(); // only one call to default constructor of T, to initialize x
T* p = new T(f()); // only one call to default constructor of T, to initialize *p
Your situation falls under the second case.

removing the const in copy constructor

This is what I did originally.
class A
{ public:
A() { std::cout << "\ndefault constructor"; }
A(const A&) { std::cout << "\ncopy constructor"; }
A(int) { std::cout << "\nconversion constructor"; }
};
A a0; // print default constructor
A a1(a0); // print copy constructor note : direct initialization
A a2 = a0; // print copy constructor note : copy initialization
A a3(123); // print conversion constructor note : direct initialization
A a4 = 123; // print conversion constructor note : copy initialization (create a temp object from int)
However, if class A is slightly modified as the following (remove const in copy constructor), why are there compile error for the final line? thank you
class A
{ public:
A() { std::cout << "\ndefault constructor"; }
A(A&) { std::cout << "\ncopy constructor"; }
A(int) { std::cout << "\nconversion constructor"; }
};
A a0; // print default constructor
A a1(a0); // print copy constructor note : direct initialization
A a2 = a0; // print copy constructor note : copy initialization
A a3(123); // print conversion constructor note : direct initialization
//A a4 = 123; // compile error
A a4 = 123;
is equivalent to
A a4 = A(123); // The RHS is a temporary A object.
That works for the first case since there is a constructor that takes a A const& as the argument type.
That does not work if the argument type is A&. A temporary object can be used when the argument type is A const&, not when it is A&.
For the case A a4 = 123;, when object “a4” is being constructed, the statement
A a4 = 123;
is broken down by the compiler as
A a4 = A(123);
In above statement, one argument constructor i.e. A(int) is used to convert integer value “123” to a temporary object & that temporary object is copied to the object “a4” using copy constructor. C++ does not allow to pass temporary objects by non-const reference because temporary objects are rvalue that can't be bound to reference to non-const.
So, generically, If you're not passing your argument with a const qualifier, then you can't create copies of const objects.
One more similar example for better understanding:
class Test
{
/* Class data members */
public:
Test(Test &t) { /* Copy data members from t*/}
Test() { /* Initialize data members */ }
};
Test fun()
{
cout << "fun() Called\n";
Test t;
return t;
}
int main()
{
Test t1;
Test t2 = fun(); //compilation error with non-const copy constructor
return 0;
}
$g++ -o main *.cpp
main.cpp: In function ‘int main()’:
main.cpp:22:18: error: cannot bind non-const lvalue reference of type ‘Test&’ to an rvalue of type ‘Test’
Test t2 = fun();
~~~^~
main.cpp:8:4: note: initializing argument 1 of ‘Test::Test(Test&)’
Test(Test &t) { /* Copy data members from t*/}
^~~~

Behaviour of copy constructor accepting const reference

In the following Code, in this line
A(A& b)
When using this compiler gives error as
c110.cpp:41: error: no matching function for call to ‘A::A(A)’
c110.cpp:8: note: candidates are: A::A(A&)
But as soon as i convert it into
A(const A& b)
Many many thanx in Advance
No error comes. Why is it so?
Code
class A
{
public:
static int cnt;
A(A& b)
{
cnt++;
cout<<"cnt="<<cnt<<endl;
}
A()
{
cnt++;
cout<<"cnt="<<cnt<<endl;
}
~A()
{
cnt--;
cout<<"cnt="<<cnt<<endl;
}
};
int A :: cnt=0;
A fun(A b)
{
return b;
}
int main()
{
A a;
A b=fun(a);
return 0;
}
Non-const references cannot bind to temporaries. If you pass a temporary as parameter, A& is illegal but const A& isn't.
The line
A b=fun(a);
does copy-initialization on the object returned by fun(a), which is a temporary.
Also, the copy constructor shouldn't take a non-const reference because, logically, you don't need to modify the object you're copying from.
I think its always safe to use A(const A&) type of syntax for copy construction rather than A(A&), because RVO may take place or not, its compiler dependent.
As in the above question RVO is not taking place and the temporary is been created, hence A(const A&) is safe to use.