c++copy-constructorcopy-elision

Number of copies when -fno-elide-constructors flag is passed to the compiler


Consider the following class:

struct IntPointerWrapper 
{
    int* data_pointer;

    IntPointerWrapper() : data_pointer(new int()) 
    {
        std::cout << "IntPointerWrapper()" << std::endl;
    }

    IntPointerWrapper(const IntPointerWrapper& other) : data_pointer(new int())
    {
        std::cout << "IntPointerWrapper(const IntPointerWrapper& other)" << std::endl;
        *data_pointer = *other.data_pointer;
    }

    IntPointerWrapper& operator=(const IntPointerWrapper& other)
    {
        std::cout << "operator=(const IntPointerWrapper& other)" << std::endl;
        *data_pointer = *other.data_pointer;
        return *this;
    }
    
    ~IntPointerWrapper() 
    { 
        std::cout << "~IntPointerWrapper() " << std::endl;
        delete data_pointer; 
    }
};

And a simple factory method

IntPointerWrapper bar() 
{
    IntPointerWrapper q;
    return q;
}


int main()
{
    IntPointerWrapper m = bar();
}

I want to compile with disabled copy elision, to see how many times the copy constructor is called.

I get this output:

IntPointerWrapper()
IntPointerWrapper(const IntPointerWrapper& other)
~IntPointerWrapper() 
IntPointerWrapper(const IntPointerWrapper& other)
~IntPointerWrapper() 
~IntPointerWrapper() 

So the copy constructor is called twice, but I'm not really getting why. Before running this experiment I would have bet that a single call were enough.

I'm wondering if there is any motivation behind these 2 copies instead of a single one, or it just something implementation specific.

I tried with Clang, GCC and MSVC and I obtain the same result.


Solution

  • With

    IntPointerWrapper bar() 
    {
        IntPointerWrapper q;
        return q;
    }
    

    you have a copy operation to copy q into the return object of the function. That is your first copy. Then you have

    IntPointerWrapper m = bar();
    

    which copies the returned value from bar() into m, so that is your second copy operation.


    It should be noted that the two copies only happens before C++17. Since C++17 we have guaranteed copy elision and that gets rid of the copy that happens in IntPointerWrapper m = bar(); and only has a copy for the returned object. You can see that difference in this live example