Barney Szabolcs
Barney Szabolcs

Reputation: 12514

Does RVO work with "new"?

Does copy elision kick in in this situation? In other words, do modern compilers with copy elision avoid to call any copy constructor here?

class BigObj{};

BigObj fun()
{
  BigObj b;
  return b;
}
int main()
{
  BigObj *pb = new BigObj(fun());
}

I aim to store an object in a pointer. The object is returned by a function. I want to store it without copying it.

I can't use c++11

Upvotes: 0

Views: 325

Answers (2)

dtech
dtech

Reputation: 49289

IMO it is not entirely clear what you aim to achieve. If dynamic allocation is what you want to use, then your function should simply:

BigObj * fun()
{
  return new BigObj;
}
int main()
{
  BigObj *pb = fun();
}

... and save yourself the trouble.

Contrary to the previous revision of the answer, it turned out that the compiler can omit a substantial amount of work as long as it is in a static context that can be thoroughly analyzed:

class C {
public:
    C() {qDebug() << "C created";}
    C(const C & o) { qDebug() << "C copied"; }
};

C foo() {
    C c;
    qDebug() << &c;
    return c;
}

...
    C c = C(foo()); // note that `C c = ` is also copy construction
    qDebug() << &c;

The output verifies that both instances have the same address, so even in the context of a local, the instance is actually not stored in the stack frame of foo.

Changing to:

C * cp = new C(foo());
qDebug() << cp;

to my surprise also output the same address, with both the return by value copy and the copy constructor omitted. c in foo is constructed directly in the memory chunk, allocated by new.

In conclusion the C++ compiler is pretty smart at analyzing and doing every possible optimization.

Turning off optimizations in the first and second case respectively:

C created
0x28fd97
C copied
C copied
C copied
0x28fdeb

...

C created
0x28fd97
C copied
C copied
0x34fd00

Upvotes: 1

Peter
Peter

Reputation: 36597

RVO is among those things that the standard permits, but does not specifically require. That said, most modern compilers (at least, with appropriately optimisation settings enabled) will implement it. If you want a guarantee, however, you will need to read your compiler documentation.

Since the aim is to dynamically allocate an object anyway, I would simply change the example so the called function does dynamic allocation. Instead of (the OP's code);

BigObj fun()
{
    BigObj b;
     //   presumably the point of fun() is that some initialisation
     //     of b occurs here
    return b;
}
int main()
{
    BigObj *pb = new BigObj(fun());
}

I would simply use

BigObj *fun()
{
    BigObj *b = new BigObj;
     //   presumably the point of fun() is that some initialisation
     //     of *b occurs here
    return b;
}
int main()
{
    BigObj *pb = fun();
}

and eliminate the potential copying of a BigObj all together. The only thing that is being copied around is the value of a pointer. The compiler therefore does not rely on presence of C++11 move constructors to optimise the above, since it avoids unnecessary creation and copying of objects, so this meets the OPs need to not use C++11.

Obviously, in either case, it would be good practice to match the usage of operator new with a corresponding operator delete.

Upvotes: 0

Related Questions