Unnecessary copies

Consider the following program:

#include <iostream>
 
class Point
{
    int m_x{};
    int m_y{};
 
public:
    Point() = default;
 
    Point(int x, int y)
        : m_x{x}, m_y{y}
    {
        std::cout << "Constructing using Point(" << x << ", " << y << ")\n";
    }
 
    // copy constructor
    Point(const Point &obj)
        : m_x{obj.m_x}, m_y{obj.m_y}
    {
        std::cout << "Using copy constructor Point(const Point&)\n";
    }
 
    void print() const
    {
        std::cout << "{" << m_x << ", " << m_y << "}\n";
    }
};
 
int main(int argc, char const *argv[])
{
 
    Point y{Point{1, 2}};
    y.print();
    return 0;
}

If we compile this program using following command:

g++-15 -ggdb -std=c++14 -fno-elide-constructors -W copy_elision.cpp -o copy_elision.out

and run the program, we should see following outputs:

Constructing using Point(1, 2)
Using copy constructor Point(const Point&)
{1, 2}

If we notice, copy constructor is unnecessarily called because the way we are initializing the Point y. It could be improved by directly list initialization as shown below:

 Point y{1, 2};

It could have saved the unnecessary copy.

Copy elision

To solve the above problem, compiler does copy constructor optimization where it rewrites the statements to avoid unnecessary copying (by not calling the copy constructor). This is called copy elision optimization. The copy constructor which did not get call because of this optimization is said to be elided.

Copy elision optimization is exempt from as-if rule (explained in compile optimizations) because it has to rewrite the statement and because of that copy constructor is not called. If there is some behavior in copy constructor, that behavior does not get executed. So, this optimization changes some behavior which is against as-if rule.

With this, we compile the same program by removing the flag -fno-elide-constructors, we should not see the output from copy constructor as compiler would elide the copy constructor to avoid unnecessary copy.

~/L/c++/constructors|main⚡?
❯ /usr/local/Cellar/gcc/15.2.0/bin/g++-15 -ggdb -std=c++14 -W copy_elision.cpp -o copy_elision.out

~/L/c++/constructors|main⚡?
❯ ./copy_elision.out
Constructing using Point(1, 2)
{1, 2}

So, when there is unnecessary copy happening such as when passing a class type by value or returning by value, the compiler would elide those copies.

References

  1. https://www.learncpp.com/cpp-tutorial/class-initialization-and-copy-elision/