3

Here is an example for friend functions found on the internet:

#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle() {}
    Rectangle(const Rectangle &r) { 
        width = r.width; 
        height = r.height;
        cout << "copy\n";
    }
    Rectangle (int x, int y) : width(x), height(y) {}
    int area() {return width * height;}
    friend Rectangle duplicate (const Rectangle&);
};

Rectangle duplicate (const Rectangle& param)
{
  Rectangle res;
  res.width = param.width*2;
  res.height = param.height*2;
  return res;
}

int main () {
  Rectangle foo;
  Rectangle bar (2,3);
  foo = duplicate (bar);
  cout << foo.area() << '\n';
  return 0;
}

Output:

24

Notice that the friend "duplicate" function creates a local variable and returns as return value to the caller. Isn't this supposed to be a local variable and is allocated on this stack? Should not it be destroyed once "duplicate" finishes execution? Is this example good?

Thanks.

feeling_lonely
  • 6,665
  • 4
  • 27
  • 53
  • 2
    Returning the variable makes a copy of it. – Barmar Nov 23 '17 at 07:41
  • 5
    Conceptually, the local variable `res` is constructed on the stack, then moved into the return value, then the local variable's destructor is run. In practice, the compiler optimizes that move away – Justin Nov 23 '17 at 07:41
  • 1
    The only case where this doesn't work is if you try to return an array, because arrays decay to pointers when you return them, and the pointer points to a destroyed object. – Barmar Nov 23 '17 at 07:44
  • @Barmar Probably just moved instead of copying, since it's also tagged as `c++11`. :) or RVO-ed. :) – Dean Seo Nov 23 '17 at 07:56
  • 2
    @DeanSeo But it's conceptually a copy, that's an optimization. – Barmar Nov 23 '17 at 07:58
  • @hebbo "Should not it be destroyed once "duplicate" finishes execution?", you have to worry about that, if `duplicate`'s return type is `Rectangle&`. – Dean Seo Nov 23 '17 at 07:59
  • @Justin One should watch those terms because "move" is conceptually different action from copying. Rectangle doesn't have move semantics. – Swift - Friday Pie Nov 23 '17 at 08:01
  • 1
    @Swift `Rectangle` has an auto-generated move constructor – Justin Nov 23 '17 at 08:01
  • @Justin. Not what I meant. It is case of RVO, not of move. Class does in this case because we didn't declared destructor or copy operator \or constructor. As soon as class would get one, the move constructor would be gone, yet compiler still would be able to do RVO. – Swift - Friday Pie Nov 23 '17 at 08:20
  • @Barmar DeanSeo Justin Swift : should not that call the copy constructor? I modified the code and added a copy constructor but it was not called. – feeling_lonely Nov 24 '17 at 09:35

2 Answers2

10

Just think of regular types:

int getInt(void) {
    int a = 5:
    return a;
}

The function does not really return the local variable a. Instead it returns a copy of a. Similarly, res is not returned by the function, but its copy.

In practice, the compiler is likely to detect your function and optimize the function by avoiding copying Rectangle.

To observe the constructor calls, compile it with

g++ -fno-elide-constructors foo.cpp -o foo

You must disable return value optimization for g++ (-fno-elide-constructors), which is a very basic optimization that'll be turned on even with -O0.

iBug
  • 35,554
  • 7
  • 89
  • 134
  • Should not that call the copy constructor? I did modify the code to add a copy constructor (see the updated code above), but the new constructor was not called. – feeling_lonely Nov 24 '17 at 09:38
  • how can I disable it? I would like to be able to verify and make sure I understand this voodoo magic. – feeling_lonely Nov 24 '17 at 09:41
  • I just did. Still no change to output. Meaning copy constructor was not called. I am using MinGW on windows btw. I will use g++ later to verify. – feeling_lonely Nov 24 '17 at 09:43
  • @hebbo Better give up. I just learned that this is a very basic optimization from `g++`. Try searching for a way to disable it. – iBug Nov 24 '17 at 09:53
  • 1
    @hebbo I managed to get output with `g++ -fno-elide-constructors`. – iBug Nov 24 '17 at 09:54
  • So, where are these returned values stored? how they survive the end of function call? what determines their scope? – feeling_lonely Nov 27 '17 at 23:25
  • @hebbo Return values are (usually) rvalues. They are immediately used in the next operation. Their acope also end shortly after the function returns. – iBug Nov 28 '17 at 03:09
3

Your function return Rectangle by value , so it should be fine.

Rectangle duplicate (const Rectangle& param)

The returned object isn't located in same storage isn't same object as one declared by local variable from abstract level of language. De-facto, as Justin says, compiler may optimize out creation of temporal object.

You would be in red, if you declared

Rectangle& duplicate (const Rectangle& param)

then function would try to return a reference to local variable.

How code is written, it is an equivalent of expression.

return Rectangle(param.width*2, param.height*2);

Copy constructor:

Rectangle(const Rectangle &r) { 
    width = r.width; 
    height = r.height;
}

should be proprly written as

Rectangle(const Rectangle &r): width(r.width), height(r.height) 
{ 
}

The ouput line is a side-effect that might be omitted due copy elision.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • Should not that call the copy constructor? I did modify the code to add a copy constructor (see the updated code above), but the new constructor was not called. – feeling_lonely Nov 24 '17 at 09:39
  • @hebbo RVO (return value optimization) and copy elision. Formally compiler may omit copy constructor , that's why you should not rely on sideeffects in body of such. I implore you to browse ISO draft and search for those. or search SO for those keywords to get examples – Swift - Friday Pie Nov 24 '17 at 11:24
  • So, where are these returned values stored? how they survive the end of function call? what determines their scope? – feeling_lonely Nov 27 '17 at 23:26
  • @hebbo those returned by value are rvalues or their equivalent, so their storage's scope is expression where call happened. – Swift - Friday Pie Dec 07 '17 at 17:36