2

I wrote this short test code

 # include <iostream>
 using std::cout;

struct P
{
  P()
   {cout << "I'm P::P()\n";};
  P(const P& p)
   {cout << "I'm P::P(const P&)\n";}
  P(P&& p)
   {cout << "I'm P::P(P&&)\n";}
};

int main()
{
  P pa, pe(pa);
  P pi((P(pa))), // this should be an object declaration
    pu(std::move(pa)); // as well as this one
  P po(P(pa)); // this is, instead, a function declaration
}

and I got the following output

I'm P::P()
I'm P::P(const P&)
I'm P::P(const P&)
I'm P::P(P&&)

I have understood all but the third line of output, coming from the instantiation of the pi object: why is the copy constructor being called and not the move constructor as it happens for the pu object?

Shouldn't the P(pa) parameter be an unnamed non-const rvalue? How can it be bound to a lvalue reference? Or the third line comes from the copy constructor instantiating the parameter itself? In this case who constructs pi ? Is there some kind of optimization ?

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
GSi
  • 649
  • 3
  • 10
  • `const` lvalue references *can* bind to temporaries. Is that what's confusing you? – Quentin Jan 09 '18 at 11:07
  • 3
    See https://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization – Holt Jan 09 '18 at 11:09
  • 1
    What Holt said. `(P(pa))` skips the creation of the temporary, and instead initializes `pi` directly from `pa`, which is an lvalue. – StoryTeller - Unslander Monica Jan 09 '18 at 11:12
  • Note: You might use `{}` to avoid vexing parse. – Jarod42 Jan 09 '18 at 12:34
  • I don't think `const` is relevant because the same output is obtained also removing the `const` qualifier from the copy constructor's signature. Rather it seems to be a "move elision" since, compiling with the `--no-elide-constructors` option the move constructor appears to be called twice. – GSi Jan 09 '18 at 14:23

0 Answers0