0

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

Barney Szabolcs
  • 11,846
  • 12
  • 66
  • 91
  • @juanchopanza, Probably he has mixed up with the language in asking. He means, the object returned from `fun()` need not be copied while creating `new BigObj(..)`. Rather use the same object. – iammilind Aug 07 '15 at 07:12
  • Thanks for pointing out that the sentence was easily misunderstandable, I hope I managed to correct it. – Barney Szabolcs Aug 07 '15 at 07:13
  • @ddriver, RVO does kick in the case of `fun()`, but it creates a new copy when `new BigObj(..)` is invoked. That can be avoided by having a move constructor inside `class BigObj`. [See my answer](http://stackoverflow.com/a/31871619/514235). – iammilind Aug 07 '15 at 07:15
  • 1
    "Copy elision" and "RVO" are not the same thing. "Copy elision" removes the intermediate temporary. "RVO" (or, more precisely, "NRVO" in this case) potentially removes `b` and all copying completely. So, what is this question supposed to be about? Copy elision? Or [N]RVO? – AnT stands with Russia Aug 07 '15 at 07:17
  • @AnT: Though, RVO is a form of copy elision, just like NRVO is a form of RVO. – Benjamin Lindley Aug 07 '15 at 07:35
  • 1
    Perhaps the question is: If we printed `&b` inside `fun`, could it be the same as `pb`? If yes, why? – Aaron McDaid Aug 07 '15 at 07:37
  • @ddriver: It wouldn't be returning a reference to a local variable if the compiler is doing it as an optimization. The object will be constructed in place, in the allocated memory: http://coliru.stacked-crooked.com/a/d526d4a058e12c0d – Benjamin Lindley Aug 07 '15 at 07:58
  • @BenjaminLindley - yes you are correct, the compiler is apparently smart enough to treat `b` in the "local context" without really being a function local in terms of its allocation site. – dtech Aug 07 '15 at 08:00

2 Answers2

1

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
dtech
  • 47,916
  • 17
  • 112
  • 190
  • It is a pretty surprising experience isn't it? I wouldn't return a pointer from a function, I wouldn't use pointers in the first place unless necessary, but I work with legacy code that does that a lot. – Barney Szabolcs Aug 08 '15 at 07:58
  • @BarnabasSzabolcs Considering how NRVO is implemented, I'm not really surprised. This also doesn't have much to do with how smart a compiler is IMO: NRVO is typically implemented by adding an additional parameter to the function, which passes the location at which to construct the return value. It doesn't matter for the callee (`fun`) whether that's stack or heap memory. In fact, clang++ applies NRVO in this case even when not inlining `fun` and at `-O0`. – dyp Aug 08 '15 at 08:35
0

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.

Peter
  • 35,646
  • 4
  • 32
  • 74