-2

I want to implement a construction which is more efficient when returning big structures by value, using an unnamed temporary ('prvalue') as an default formal parameter. First I'll show an example of the performance problem which it will solve:

If we have a structure:

struct BIG_STRUCT
{
    char aArray[260];
    char aArray1[260];
} ;

And a function that initialize a temporary of it and then return it by value like this:

BIG_STRUCT Func()
{
    BIG_STRUCT sTemp;

    /*init sTemp*/

    return sTemp; // or BIG_STRUCT(sTemp) // constructs the return value by using a temporary
}

I believe now you saw where the performance issue is - 2 copies of BIG_STRUCT are created on the stack - 1 for the temporary used to 'construct' the return value and 1 for the return value itself. Also at the return statement a 'copy constructor' is called to initialize the return value using the temporary. So both memory and execution time are wasted. Now you maybe think why not return by reference? But this will result in a dangling reference as function temporaries are 'destructed' at the end of the function scope they belong to (it wouldn't work even it the reference is 'rvalue' one). Example:

BIG_STRUCT & /* or 'rvalue' one - '&&' */ Func()
{
    BIG_STRUCT sTemp;

    /*init sTemp*/

    return sTemp; // return a local by reference - resulting in dangling reference
}

The solution of this problem as I said at the beginning by creating the return value temporary ourselves at the function call, using an unnamed variable as a default formal parameter with type 'rvalue' reference and returning again an 'rvalue' reference to this object. So the optimized version of 'BIG_STRUCT Func()' will looks like this:

BIG_STRUCT &&Func(BIG_STRUCT &&result = BIG_STRUCT())
{
    //BIG_STRUCT sTemp;

    /*init result*/

    return (BIG_STRUCT&&)result; //converts 'lvavlue' to 'xvalue'
}

This will work the same way as if the function is actually returning a value but without the performance issues. There is only one problem with this construction which is that the function returns an 'rvalue' reference so if we assign it's return value to 'rvalue' reference it's life-time wouldn't be extended and this will result in a dangling reference. What I mean is that the new standard allow us to extend 'prvalues' life-time by assigning them to an 'rvalue' reference. But we are not returning one in the current example, so code like this will be invalid:

BIG_STRUCT && refPrvalue = Func();

refPrvalue.aArray[3] = 0; //unknown result accessing dangling reference

Any solution how can I fix this problem?

AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66
  • It's not a duplicate. I'm asking an entirely different question. Do you mind reading it first before judging? – AnArrayOfFunctions Nov 24 '14 at 23:03
  • 1
    Already re-opened just for that reason. :) –  Nov 24 '14 at 23:04
  • How about just using a reference parameter that's filled and making the fuction void? – Hatted Rooster Nov 24 '14 at 23:06
  • Because this form can be used in expressions. Why C++ functions do have a return value - ask yourself? – AnArrayOfFunctions Nov 24 '14 at 23:09
  • Is is necessary to use a raw array? You could use `std::array` instead and rely on RVO. – davidhigh Nov 24 '14 at 23:12
  • "I believe now you saw where the performance issue is - 2 copies of BIG_STRUCT are created on the stack - 1 for the temporary used to 'construct' the return value and 1 for the return value itself." -- Not necessarily. You may want to read up on return-value optimisation, or [this entry in the C++ FAQ](http://www.parashift.com/c++-faq/return-by-value-optimization.html). –  Nov 24 '14 at 23:12
  • Did you actually see in the compiled machine code that the data was being copied? – Kerrek SB Nov 24 '14 at 23:21
  • This is a "solution" in search of a problem. – T.C. Nov 24 '14 at 23:26
  • Does it matter? Maybe it will at least reduce compiler work and thus compiling time. – AnArrayOfFunctions Nov 26 '14 at 11:03

1 Answers1

1

I think you might have misunderstood how return by value works in C++11.

Returning a local object with automatic storage duration will automatically be considered as an rvalue by the compiler (this is required by the standard).

MyObject func() {
    MyObject o;

    return o; // Return value will be move constructed.
}

This happens as the compiler knows that o is gong to be destructed when exiting the scope. Thus it is wrong to explicitly pass an rvalue to return in this situation, e.g.

return std::move(o); // Wrong, 'o' is implicitly converted to rvalue.

In fact this will prohibit RVO (see next paragraph) and is thus a pessimization.

Additionally, all most modern compilers will make use of RVO (Return Value Optimization), which will make sure that the returned value is constructed at the call site. E.g.

MyObject func() {
    return {}; // Further simplification. Default constructs return value.
}

int main() {
    MyObject o = func(); // You can almost be sure that no copies are made.
                         // 'o' is constructed directly at call site (RVO).
                         // Worst case scenario, one move is performed.
}
Felix Glas
  • 15,065
  • 7
  • 53
  • 82