Returning an object by reference

When returning an object by reference, only the address of the returned-object is returned, and that way we spare pushing a large object into the stack, and also spare time of pushing and popping large object to/from stack.

But what happens when the object that receiving the returned-object, is not a reference, but a 'regular' object?

How is the content of the returned object copied into the receiving object?

See for example in main, wid vs rwid.
(I know in the case the returned-object is just one variable, there's no need to return it by reference, but its for simplifying the code).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Rectangle
{
public:
	Rectangle(int w=0, int h=0);
	const int& getWidth(void) const;
private:	
	int m_w;
	int m_h;
};

const int& Rectangle::getWidth(void) const
{
   return this->m_w;
}

Rectangle::Rectangle(int w, int h):m_w(w), m_h(h) {}

int main()
{
   Rectangle rect;
   int wid;
   int& rwid = rect.getWidth(); //simply coping the address of returned-object into rwid.
   
   wid = rect.getWidth(); //what happens here?

   return 0;
}
Last edited on
that way we spare pushing a large object into the stack, and also spare time of pushing and popping large object to/from stack.

That doesn't usually happen when you return an object by value either.

How is the content of the returned object copied into the receiving object?

References are aliases: since the return of rect.getWidth() aliases rect.m_w, line 24 is processed as if you wrote wid = rect.m_w;. Like any assignment, it replaces the contents of the object to the left (wid) with the contents of the object to the right (rect.m_w).
Last edited on
@Cubbi
Thank you very much!


"that way we spare pushing a large object into the stack, and also spare time of pushing and popping large object to/from stack."

That doesn't usually happen when you return an object by value either.


so what is the advantage of returning by reference over returning by value?

I mean, when you return by value, a copy of the returned value is created somewhere, then it is copied into the receiving object, and then deleted from where it was stored.

If it's not stored in the stack, then where?


References are aliases: since the return of rect.getWidth() aliases rect.m_w, line 24 is processed as if you wrote wid = rect.m_w;. Like any assignment, it replaces the contents of the object to the left (wid) with the contents of the object to the right (rect.m_w).


Awesome Explanation, thank you.

do you know how its carried out wid = rect.m_w;.

The compiler performs some kind of memcpy?

In that case of returning by reference, then no extra copy is created, right?
Last edited on
so what is the advantage of returning by reference over returning by value?

Return by non-const reference is common, and it tells the caller that the function-call expression itself is an lvalue. That's how you can implement operator[] to make something like m[key] = value; compile. Another common example are the I/O operators: they return ostream/istream by non-const reference so that they can be chained: cout << a << b << c;

Return by const reference, as in your case, is not something I've seen much: it makes the function call a const lvalue, so the caller can take its address and it will be the address of the referenced object: &rect.getWidth() == &rect.m_w;. I suppose you could use it to call const member functions on the returned type without building a temporary.

a copy of the returned value is created somewhere, then it is copied into the receiving object, and then deleted from where it was stored.

in most cases, it is unified with the receiving object, see for example
http://en.cppreference.com/w/cpp/language/copy_elision
In addition, since C++11 it is also guaranteed to be moved into the existing object where copy elision isn't possible but a move is.

do you know how its carried out wid = rect.m_w;.
The compiler performs some kind of memcpy?

In this case (int) it will copy the value. Your main() does nothing so machine code won't be issued in any case, but had you done something with wid later (so that wid would have been assigned a CPU register), and had rect been in RAM, this would be a single CPU load instruction. Same as if you'd returned the int by value:

on Intel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Rectangle
{
public:
    Rectangle(int w=0, int h=0) : m_w(w), m_h(h) {}
    const int& getWidth() const { return m_w; }
    int getWidthByValue() const { return m_w; }
private:
    int m_w;
    int m_h;
};

Rectangle rect;
int main()
{
   volatile int wid = 0;
   volatile int wid_by_value = 0;
   wid = rect.getWidth();
   wid_by_value = rect.getWidthByValue();
}
main:
movl    rect(%rip), %eax
movl    $0, -8(%rsp) // volatile int wid = 0
movl    $0, -4(%rsp) // volatile int wid_by_value = 0
movl    %eax, -8(%rsp) // wid = rect.getWidth();
movl    %eax, -4(%rsp) // wid_by_value = rect.getWidthByValue();
xorl    %eax, %eax
ret
@Cubbi

Thanks a lot again!!!

You had me googling up what's rvalue, lvalue, so I definitely learned MUCH from your post :)

What you're saying actually is that when returning by value, returning const-reference, and returning reference,

you'll get the same time and space efficiency?

i.e the 5 cases:

1. return by value:
1
2
int foo() {..}
int var = foo()


2. return const reference to variable:
1
2
const int& foo() {..}
int var = foo()


3. return const reference to reference:
1
2
const int& foo() {..}
const int& var = foo()


4. return reference to variable:
1
2
int& foo() {..}
int var = foo()


5. return reference to reference:
1
2
int& foo() {..}
int& var = foo()


I always thought that:
3) is better than 4), 2) and 1)
5) is better than 4), 2) and 1)


I'm really confused because i've been always told that you should return an object / struct (or get an object / struct) by reference / pointer, and NOT by value.
Last edited on
Semantics come first: do you want a function to produce a new value or do you want to refer the caller to an existing (possibly modified) value?

For example, addition always makes a new value: the result of a+b is neither a nor b, not a member of a, or anything else pre-existing. It is new. Thus, operator+ returns by value.

Depending on how that value is used by the calling code, there may or may not be copying involved, but it's just what's required to implement the semantics.
@Cubbi

Thank you very much!
Topic archived. No new replies allowed.