Home > Enterprise >  Are compilers optimizing out all temporary objects?
Are compilers optimizing out all temporary objects?

Time:05-24

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;
    }

};

Demo

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";
}
  • Related