ManUser123
ManUser123

Reputation: 51

Prevent object copy on return

I have this:

struct Point
{
    size_t x;
    size_t y;
    ~Point()
    {
        std::cout << "Destro" << "\n";
    }
};

const Point& getPoint()
{
    return { 100, 120 };
}
int main()
{
    Point p = getPoint();
    std::cout << "Exit" << "\n";
}

and the result is

Destro
Exit
Destro

I'm basically trying to make the getPoint method not have to copy the Point class. Here is what's happening so far:

  1. Point Created
  2. Point copied to the result
  3. Point destroyed

How can I make it so that Point is only destroyed once?

Upvotes: 1

Views: 94

Answers (2)

Francis Cugler
Francis Cugler

Reputation: 7905

Here's an example of what it looks like you're trying to achieve...

#include <iostream>

struct Point {
    size_t x;
    size_t y;
    ~Point() { std::cout << "Destructor called\n"; }

    // Explicitly deleting the Copy Constructor only to illustrate a point.
    Point(const Point& other) = delete;
};

const Point makePoint(size_t a, size_t b) {
    return Point{ a, b };
}

int main() {
    Point a = makePoint(3, 5);
    std::cout << a.x << ',' << a.y << '\n';
}

Output:

3,5
Destructor called

If you noticed, I explicitly deleted the Copy Constructor and left the Assignment Operator undeclared so that it will use the default. The struct is still using the default constructor and I didn't define a User Defined constructor.

In makePoint() I added two parameters/arguments to the function so that the user can set any values to the Point object. The function is returning a const Type object. The return statement in the function is using:

return Point{a, b};

instead of

return Point(a, b);

The latter will fail to compile because there is no user-defined constructor. However, the former works because I'm using brace-initialization. This will allow construction of the object on return instead of creating a temporary on the stack frame of the function and copying that temporary as its return value. This is a form of Copy Elision.

Now, if you want to be able to copy this object, you can remove the deleted constructor and allow the class to use its default copy constructor. The semantics of the Copy Elision will still work.

This will have the same exact results. I showed the explicit deletion of the Copy Constructor to initially prevent any Copying semantics. Then explained how the code works so you can take the original above and remove the line for the deleted copy constructor and it will still work the same:

struct Point {
    size_t x;
    size_t y;
    ~Point() { std::cout << "Destructor called\n"; }
};

const Point makePoint(size_t a, size_t b) {
    return Point{ a, b };
}

The functionality or behind the scenes magic is happening within the function makePoint() and that it returns a const object and uses brace-initialization of the class.

Upvotes: 0

NathanOliver
NathanOliver

Reputation: 180500

You need to return by value instead of reference. Using

Point getPoint()
{
    return { 100, 120 };
}

Allows C++17's guaranteed copy elision to kick in which causes Point p = getPoint(); to act as if it was Point p{ 100, 120 };


Side note: Never, Never, Never, return a function local object by reference. That object will be destroyed at the end of the function leaving you with a dangling reference and using that is undefined behavior.

Upvotes: 6

Related Questions