Can returning a braced enclosed initializer lead to a copy in C++?
c++, c++11, initializer-list, return-value-optimization
Solution
Consider the following example:
#include <iostream>
struct foo {
foo(int) {}
foo(const foo&) { std::cout << "copy\n"; }
foo(foo&&) { std::cout << "move\n"; }
};
foo f() {
//return 42;
return { 42 };
}
int main() {
foo obj = f();
(void) obj;
}
When compiled with gcc 4.8.1 with `-fno-elide-constructors` to prevent RVO the output is
move
If in `f` the return statement without curly braces is used then, then the output is
move
move
With no RVO, what happens is the following. `f` must create a temporary object of type `foo`, let's call it `ret`, to be returned.
If `return { 42 };` is used, then `ret` is direct initialized from the value `42`. So no copy/move constructor was called so far.
If `return 42;` is used, then another temporary, let's call it `tmp` is direct initialized from `42` and `tmp` is moved to create `ret`. Hence, one move constructor was called so far. (Notice that `tmp` is an rvalue and `foo` has a move constructor. If there was no move constructor, then the copy constructor would be called.)
Now `ret` is an rvalue and is used to initialize `obj`. Hence the move constuctor is called to move from `ret` to `obj`. (Again, in some circumstances, the copy constructor could be called instead.) Hence either one (for `return { 42 };`) or two (for `return 42;`) moves happen.
As I said in my comment to the OP's question, this post is very relevant: construction helper make_XYZ allowing RVO and type deduction even if XZY has noncopy constraint. Especially the excelent answer by R. Martinho Fernandes.
Problem
Example: ``` struct s { int a; }; s func() { return {42}; } int main() { s new_obj = func(); // line 6 (void) new_obj; return 0; } ``` This works. Now, what happens, if we assume that our compiler does no RVO? - `func` returns a struct of `s`, so `{42}` must be converted to `s`, is then returned and finally copied to `new_obj` in line 6. - `func` returns an initializer list, so a deep copy is impossible. What does the language say? Can you give a proof? Note: I know that this does not seem useful in this example, but for returning very large, constant sized `std::array`s, I do not want to rely on RVO.