-1

Let's consider two functions:

//Test functions
Object MakeObj(){/* processsing */};
Object ChangeObj(const Object& obj){/* processsing */};
//Then execute
Object test_obj = ChangeObj(MakeObj());
  1. Is the execution safe?

  2. Where is stored the 'Object' return value of the MakeObj()? Can I use a reference to that storage?

  3. Or a temporary variable is necessary:

    Object MakeObj(){/* processsing */};
    Object ChangeObj(const Object& obj){/* processsing */};
    Object tmp_obj = MakeObj();
    Object test_obj = ChangeObj(tmp_obj);
    
  4. Is there any performance gain or it is exactly the same, if the code from point 3 gets changed:

    Object MakeObj(){/* processsing */};
    Object ChangeObj(Object obj){/* processsing */}; //No reference here, so 'Object' value is created.
    Object test_obj = ChangeObj(MakeObj());
    
  5. Is there a way to use "move" to avoid coping a big Object? E.g:

    Object ChangeObj(Object&& obj){/* processsing */};
    

    If yes, how it would look like?

  6. What would be the fastest implementation (as less coping as possible) of those two functions above? Assumption: "Object MakeObj();" declaration cannot be changed.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 8
    `Object test_obj = ChangeObj(MakeObj());` doesn't compile, so you don't to worry about it. – Lukas-T Jun 09 '21 at 06:19
  • 1
    `&&` isn't just a "move" reference, but also called a "generic" or even "rvalue" reference. It's called rvalue reference because you can use it for rvalues, like what `MakeObj()` returns. – Some programmer dude Jun 09 '21 at 06:20
  • 1
    When some object is returned from a function, it typically works such that the caller allocates storage for this object on its stack and then passes its address as a hidden argument to the called function. This function then may (or must) create the returned object directly in this storage. Live demo: https://godbolt.org/z/KEhqvvKoW (address of the storage is passed in `rdi` to `f`). However, generally, this whole problem is much more complicated (for instance, small trivial objects are usually passed in registers). – Daniel Langr Jun 09 '21 at 06:27
  • what is `ChangeObject` doing? Why is it taking the parameter by non-const reference *and* returns an `Object` ? – 463035818_is_not_an_ai Jun 09 '21 at 07:18
  • 1
    The edit to make the argument a **`const`** reference changes quite a lot. I'd argue that change makes this a whole new question. – Some programmer dude Jun 09 '21 at 09:16

1 Answers1

2
  1. Execution is safe because const reference extends object lifetime(or here)

  2. The object itself is stored somewhere on the stack, but it will not be erased as long as the const reference exists(compare asm line 20(non-reference) and 25-27(const reference))

  3. No. It isn't necessary.

  4. In the general case, the code from point 4 is faster, even if there was a move (in the case of a vector, optimization for 2-3 assignment operations size_t)

  5. move can only be applied in the code from point 3, and then it will look like

Object MakeObj(){/* processsing */};
Object ChangeObj(Object&& obj){/* processsing */};
Object tmp_obj = MakeObj();
Object test_obj = ChangeObj(std::move(tmp_obj));
//here tmp_obj is no longer valid
  1. Code from point 4 should be the fastest, but MEASURE!!! And remember about first rule of optimization: Don't optimize prematurely
Deumaudit
  • 978
  • 7
  • 17
  • I realized that the code from the introduction is the fastest, so faster than point 4 because it is one 'Object' variable creation less. Thanks for the answer. – Jerzy Jamroz Jun 09 '21 at 09:57
  • 1
    But RVO should optimize that excesive object creation @JerzyJamroz – Deumaudit Jun 09 '21 at 10:32