1

Ignoring the copy/move elision from the compiler, I would like to know if the following code (assuming foo has a constructor accepting three ints) "syntactically" creates a temporary object and then copy/move initializes the function argument, or directly calls the constructor:

void acceptsFoo(foo a);

acceptsFoo({1, 2, 3});

And what about this case?

//ignoring RVO optimization
foo returnsFoo()
{
   return {1, 2, 3};
}

I know that the code below, even without copy/move elision, is the same as calling the constructor so will not generate any temporary, but I couldn't find info about the code above.

foo = { 1, 2, 3 } //assuming the constructor is non-explicit
foo { 1, 2, 3 }
yggdrasil
  • 737
  • 5
  • 14
  • Do you want a theoretical answer or a practical one? – YSC Sep 12 '18 at 15:44
  • A theoretical one, if possible. I know that pratically it will always be elided by any proper compiler. – yggdrasil Sep 12 '18 at 15:45
  • 1
    In this case, you should provide a complete example (no "assuming X exists") and tag your question with language-lawyers. – YSC Sep 12 '18 at 15:46
  • @A.S. Certain elisions are required by the language. To ignore *all* elisions will not give you a meaningful answer. Though you could assume *optional* elisions do not occur. – François Andrieux Sep 12 '18 at 15:47
  • And finally, bound your question to c++98, c++11, c++14, c++17 or c++2a. – YSC Sep 12 '18 at 15:47
  • @FrançoisAndrieux: "*Certain elisions are required by the language.*" No, they're not. Elision of any form *by definition* is optional. Even C++17's "guaranteed elision" is a misnomer. It works by redefining the meaning of a prvalue so that temporary objects that might have been elided simply are not generated. – Nicol Bolas Sep 12 '18 at 16:19
  • There are special cases outside of the examples in the OP, sort of: [See `std::initializer_list`](https://en.cppreference.com/w/cpp/utility/initializer_list). E.g. passing an initializer list through an `auto` parameter will resolve to an `std::initializer_list` backed internally by a temporary `T[]`. – Jason C May 10 '22 at 11:59

2 Answers2

2

When a braced-init-list is used to initialize an object, it is used to initialize the object. Period.

Applying a braced-init-list to a function parameter means to initialize that parameter with the list of values, in accord with the rules of list initialization. When you return a braced-init-list, it is used to initialize the return value object with the list of values, in accord with the rules of list initialization.

There is no temporary object theoretically being copied into the parameter/return value.

Now (pre-C++17), if you had done acceptsFoo(foo{1, 2, 3}); or return foo{1, 2, 3}, then that would provoke the creation of a temporary, which would then be used to initialize the parameter/return value.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • This is exactly the answer I was looking for (thanks to user2079303 too for the equally good answer). Together with the answer here https://stackoverflow.com/questions/38043319/how-does-guaranteed-copy-elision-work everything became clear :) – yggdrasil Sep 13 '18 at 01:10
1
void acceptsFoo(foo a);

acceptsFoo({1, 2, 3});

No, there would not be a temporary in this case. The parameter is initialized directly from the argument expression.


//ignoring RVO optimization
foo returnsFoo()
{
   return {1, 2, 3};
}

Just like in the case of the argument, the return value is initialized directly from the return-statement.

However, the result of the function call expression would be a temporary object, yes. So, if you called the function like this: foo f = returnsFoo(); There would be two instances created. First the return value is initialized from the brace-initializer, then the variable bound object is copy initialized from the temporary (by move, if foo is movable).

That is from the perspective of the abstract machine; the copy/move could be elided in practice (this is what RVO does).


However starting from C++17, in the statement foo f = returnsFoo();, there would be no temporary and no copy/move to elide. On the other hand, in the statement returnsFoo();, there would be a temporary created (which is destroyed immediately).

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326