Some people say compilers are smarter than humans in most cases and will optimize lots of stuff than we can explicitly do so. I wanna know if the compiler is optimizing this out. The code below have interesting result but didn't have error. But have a serious problem.
//will this move or copy construct?
#include <iostream>
class A
{
public:
A()
{
std::cout << "Constructed A.\n";
}
~A()
{
std::cout << "A destroyed.\n";
}
};
class B
{
private:
A m_A;
public:
B(A someA):m_A{someA}
{
}
};
int main()
{
B oneB{A()};
return 0;
}
This code print on Windows 10 using Clang 13.0.1 with -fexceptions -O3 -Wall -g -std=c 20 -v -c
Constructed A.
A destroyed.
A destroyed.
Why is A destroyed twice but constructed only once?
This happen even when constructing B
using constant reference.
This is getting ridiculous. I'm still learning C and never been in any projects or whatsoever. I'm asking something I'm confused about while learning online.
CodePudding user response:
Why is A destroyed twice but constructed only once?
The A
that is destroyed the second time is different from the A
destroyed the first time. You can confirm/verify this by adding a copy constructor to your class A
as shown below. The copy constructor will be used to initialize m_A
in the m_A{someA}
of the member initializer list.
class A
{
public:
//other members as before
//copy constructor added
A(const A&)
{
std::cout<<"copy ctor"<<std::endl;
}
};
After adding the copy constructor the output of the program will look like:
Constructed A.
copy ctor
A destroyed.
A destroyed.
Note that you're using C 20 which has mandatory copy elison(from C 17 & onwards). This means that when you wrote:
B oneB{A()};
in C 20(&C 17) there is no creation of a temporary object and the parameter A someA
of B::B(A)
is created directly without having to copy any temporary.
But prior to C 17, there was non-mandatory copy elison. This means that a temporary A
object will be created which will be copied/moved to the parameter named someA
. But the compilers were allowed to elide this copy/move construction as an optimization.
To verify this, you can pass the -fno-elide-constructors
flag to the compiler in C 11 or C 14(which will tell the compiler to not do the optimization involving copy/move construction) and you will see that this is indeed what happens as shown in the given demo link:
Demo C 11 with fno-elide-constructors.
The output of the program with C 11 and -fno-elide-constructors
flags will be:
Constructed A.
copy ctor
copy ctor
A destroyed.
A destroyed.
A destroyed.
Note that the flag -fno-elide-constructors
will only affect the output of your program with C 11 using pre-C 17 version of the standard. From C 17 and onwards, there will be no extra call to the copy constructor. Demo C 17
CodePudding user response:
You are wrong, it is not constructed once, but twice as well: First time as temporary object, second time when copying into the B object.
However the copy constructor used for is generated implicitly and doesn't provide any output. Add one explicitly and you'll see:
A(A const&)
{
std::cout << "Copied A.\n";
}