Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can returning a braced enclosed initializer lead to a copy in C++?

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?

  1. 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.
  2. 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::arrays, I do not want to rely on RVO.

like image 336
Johannes Avatar asked Feb 06 '14 13:02

Johannes


1 Answers

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.

like image 162
Cassio Neri Avatar answered Sep 30 '22 02:09

Cassio Neri