Const Reference To Temporary Is Useless

C++ is always full of surprises. This week, I learned that C++ has (yet another) special feature that allows the lifetime of a temporary (rvalue) to be lengthen on the stack if it is bound to a reference-to-const.

It means that the following example is legal C++. In this example, the temporary i’s lifetime is extended across its original scope.

int f()
{
	int i = 0;
	return i;
}
void main()
{
	int const &cr = f(); // lifespan extended
	int &cr = f(); // illegal (crash) because f() does not return lvalue
}

This is supposed to minimize the number of copies performs.

More Ammo To Blow Your Leg Away

You would think that this feature prevents the temporary from being copied. Apparently not so.

If you run the following program on VC9.0 and VC10.0, obj destructor is called twice, implies that it was copied. [sidenote: gcc 4.3 did not perform the copy]

#include <iostream>
class obj
{
public:
	~obj() { std::cout <<"destroyed" << std::endl; }
};
void main()
{
	obj const &rco = obj();
}

This is because the standard allows the temporary to be fully copied, and renders the reference-to-const declaration useless.

Here’s the C++ standard’s guideline in section 8.5.3.

If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation defined):

The reference is bound to the object represented by the rvalue or to a subobject with that object

A temporary of type “cv1 T2” is created, and a constructor is called to copy the entire rvalue object into the temporary. This reference is bound to the temporary or to a subobject with the temporary.

No Better Than RVO

Dave Abraham’s copy ellision example demonstrates how smart modern compilers have become. Return Value Optimization works well in both named and unnamed temporaries, and compose nicely.

So why bother adding reference-to-const if it works just as well without it?

Final Thoughts

C++0x (C++0a?) is supposed to address this problem by enforcing the reference-to-const to always bind to the rvalue, instead of its copy. But if a copy can be elided, I expect RVO would have done it as well.