Ben
Ben

Reputation: 4666

Do C++ compilers avoid copying when returning by value?

Consider the following code:

LargeObject getLargeObject()
{
    LargeObject glo;
    // do some initialization stuff with glo
    return glo;
}

void test()
{
    LargeObject tlo = getLargeObject();
    // do sth. with tlo;
}

A simple compiler would create a local LargeObject glo on the getLargeObject() stack and then assign it to tlo in test() when returning, which involves a copy operation.

But shouldn't a clever compiler realize that glo is going to be assigned to tlo and thus just use tlo's memory in the first place to avoid the copy operation? Resulting in something (functionally) like:

void getLargeObject(LargeObject &lo)
{
    // do init stuff
}

void test()
{
    LargeObject lo;
    getLargeObject(lo);
}

My guess is, that compilers do something similar. But can it always be done? Are there situations where it can't be optimized like that? How can I know if my return value is copied or not?

Upvotes: 1

Views: 275

Answers (3)

James Kanze
James Kanze

Reputation: 153919

For starters, even a naïve compiler will not “assign to tlo”, since the standard doesn't allow it. The formal semantics of your code involves two copies (both using the copy constructor); the first from glo to a temporary return value, and the second from this temporary return value to tlo. The standard, however, formally gives compilers the right to eliminate both of these copies, in this specific case, and practically speaking, I imagine that all compilers do.

The first copy can be suppressed anytime you return a local variable or a temporary; some compilers don't do it if there is more than one return in the code, however (but that will never be the case in well written code).

The suppression of the second copy depends on the fact that you are constructing a new object at the call site. If you're not constructing a new object, then there may not even be a second copy to suppress; e.g. in a case like getLargeObject().memberFunction(). If you're assigning to an existing object, however, there's not much the compiler can do; it must call the assignment operator. If the assignment operator copies, then you get that copy.

Upvotes: 2

Benjamin Lindley
Benjamin Lindley

Reputation: 103703

Your guess is correct. And yes, there are situations where it cannot be done, for example:

LargeObject getLargeObject()
{
    LargeObject glo1, glo2;
    // do some initialization stuff         
    if (rand() % 2)
        return glo1;
    return glo2;
}

It can't be done there because the compiler can't know whether it will use glo1 or glo2 for the return value.

"How can I know if my return value is copied or not?"

Two ways I can think of. You could create noisy copy constructors. That is, copy constructors that have some detectable side effect, like printing a message. Then of course there's the old look at the assembly.

Upvotes: 4

John
John

Reputation: 2326

Yes it should. This is called the named returned value optimization (NRVO or just RVO).

Upvotes: 2

Related Questions