Hi folks,
this might come off as a silly question but here it is.

If I have this code:

class CtrTest {

public:
	CtrTest() {
		cout << "Ctr" << endl;
	}
	CtrTest(const CtrTest&) {
		cout << "Cp Ctr" << endl;
	}
};

class Wrap {
	CtrTest test;
public:
	const CtrTest& ret_ref() { cout << "ret_ref()" << endl; return test; }
	const CtrTest* ret_ptr() { cout << "ret_ptr()" << endl; return &test; }
	const CtrTest ret_val() { cout << "ret_val()" << endl; return test; }
};

int main() {
	Wrap w;
	cout << "phase 1" << endl;
	const CtrTest test1 = w.ret_ref();
	cout << "phase 2" << endl;
	const CtrTest& test2 = w.ret_ref();
	cout << "phase 3" << endl;
	const CtrTest* test3 = w.ret_ptr();
	cout << "phase 4" << endl;
	const CtrTest test4 = w.ret_val();

	return 0;
}

yielding this output:

Ctr
phase 1
ret_ref()
Cp Ctr
phase 2
ret_ref()
phase 3
ret_ptr()
phase 4
ret_val()
Cp Ctr

I notice that, despite what I've seen written online, doing: const CtrTest test1 = w.ret_ref(); and const CtrTest* test3 = w.ret_ptr(); is not the same at all as far as efficiency is concerned.
In the first case the copy constructor for CtrTest gets called to assign the reference to test1. In the second case it is not.

What I'm asking is: is it really recommended to return objects by reference and assigning them to "plain" variables (i.e. not references like "test2") as I've seen done countless times? Wouldn't it be better to use pointers then?

I've also noticed that ret_val() yields the same result as ret_ref(), but I chalk it up to compiler optimisations.

Regards.

You seem to be treating as a single step, get the return value of a function, what is actually 2 steps

  1. Return a value from a function

  2. Assign that value to a variable

Both have associated efficiencies as follows

Return a value from a function

  • Return by value - not efficient, but sometimes necessary. sometimes you will end up with a nameless temporary object, it is always a posibility. The return value will have to be copied into the nameless temporary object (copy constructor call) and then deleted in the calling function once it has been used.

  • Return by pointer - efficient, only a pointer is returned, a few bytes and there is no need for any constructor destructor calls.
  • Return by reference - again efficient, there is no need for any constructor/destructor calls. How references are implemented is not specified anywhere (that I know of) but it seems likely that internally they will hold a pointer or something similar so again only a few bytes are required.

Assignment
What is important when considering assignment efficiency is what you are assigning to

  • An object - not efficient, there will need to be either a copy constructor or assignment operator call.

  • A pointer - efficient, no method calls on the class just copying the pointer data, a few bytes.
  • A reference - again efficient, no method calls on the class just copying the reference data, a few bytes.

Basically returning by value is less efficient, storing in an object is less efficient.

Your program does not perform a complete test because it does not return a pointer and assign it to an object. if it did you would find that to be similar to returning a reference and assigning it to an object.

Basically pointer and references have similar efficiency but references are considered to be safer becaus it is harder to create an invalid one. this is illustrated by pahse 2 and phase 3 of your program.

One final point you should be very careful about returning a object and assigning it to a pointer or reference because it would be very easy to get a pointer/reference to a temporary object that way which would most likely spell disaster for the program.

Edited 6 Years Ago by Banfa: n/a

You seem to be treating as a single step, get the return value of a function, what is actually 2 steps

  1. Return a value from a function

  2. Assign that value to a variable

Both have associated efficiencies as follows

Return a value from a function

  • Return by value - not efficient, but sometimes necessary. sometimes you will end up with a nameless temporary object, it is always a posibility. The return value will have to be copied into the nameless temporary object (copy constructor call) and then deleted in the calling function once it has been used.

  • Return by pointer - efficient, only a pointer is returned, a few bytes and there is no need for any constructor destructor calls.
  • Return by reference - again efficient, there is no need for any constructor/destructor calls. How references are implemented is not specified anywhere (that I know of) but it seems likely that internally they will hold a pointer or something similar so again only a few bytes are required.

Assignment
What is important when considering assignment efficiency is what you are assigning to

  • An object - not efficient, there will need to be either a copy constructor or assignment operator call.

  • A pointer - efficient, no method calls on the class just copying the pointer data, a few bytes.
  • A reference - again efficient, no method calls on the class just copying the reference data, a few bytes.

Basically returning by value is less efficient, storing in an object is less efficient.

Your program does not perform a complete test because it does not return a pointer and assign it to an object. if it did you would find that to be similar to returning a reference and assigning it to an object.

Basically pointer and references have similar efficiency but references are considered to be safer becaus it is harder to create an invalid one. this is illustrated by pahse 2 and phase 3 of your program.

One final point you should be very careful about returning a object and assigning it to a pointer or reference because it would be very easy to get a pointer/reference to a temporary object that way which would most likely spell disaster for the program.

Whilst what you have said here is largely true, it is not quite as black and white as you paint it. Modern compilers can often elide copies of objects if they are able to determine that the copy is not required, for example, by constructing the return of a function directly into the variable being assigned to, meaning that in certain circumstances return by value can actually be highly efficient. The new standard of C++ take sthis further by standardising the situations under which compilers are allowed to do this. See here for an interesting discussion of this topic...

http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

Also, when binding a temporary object to a reference, the lifetime of the temporary is extended to the lifetime of the reference. This is guaranteed by the language and is used to leverage such useful techniques as Scope Guards, an invaluable tool used to help write exception safe code. Google Scope Guard for more info here.

I will get round to reading that article once I get home, its a bit long to interrupt work for.

I was trying to keep things simple by not talking about the compilers ability to elide object copies/constructions when returning from functions I was aware of it though. The fact remains that if the result is assigned to an object a minimum of 1 object assignment or instatiation must happen, obviously how efficient that is rather depends on the object in question and what data it contains.


I must say I am glad to here about the extension of the lifetime of anonymous temporary objects when assigned to a reference, otherwise there was a rather large and obvious hole in the references are safe paradgim. Now all I need to do is get used to using it because there are some bits of C++ that just seem unnatural to a C programmer.

I have to ask does this lifetime extension continue to work if, for instance, that reference is then returned from the current function?

You may need to check into that further yourself. I too am at work :). See the section on references the C++ programming language which states...

A temporary created to hold a reference initializer persists until the end of its reference’s scope.

for more info.

This article has been dead for over six months. Start a new discussion instead.