Temporary Variables: Keep Your Values Close, and Your References and Pointers Even Closer
As you know, in programming C++, it is much better to return a reference to an object than it is to return that object by value. As we will see in this article, it is also much better to pass a function parameter by reference than it is to pass it by value. But there are exceptions to this, as Jun Nakamura explains.
Temporary Variables: Keep Your Values Close, and Your References and Pointers Even Closer - The cost of implicit conversion (Page 6 of 6 )
C++ functions are "friendly" enough to take any object that can implicitly be converted to the parameter it accepts according to its declaration. These conversions can only be made on parameters passed by value or by const reference because these are the only ones that are under full control of the compiler.
Let’s extend the code we have been using to test object slicing. First, here is a simple class we will call Empty:
Implicit Conversion test. Empty object created. calling CallByValue() Base created from Empty object. Base::foo() called. Base destroyed. calling CallByReference() Base created from Empty object. Base::foo() called. Base destroyed.
The conversion constructor has made it possible to use the two functions that accept a Base class by passing an Empty object, either by value or by const reference. This was possible because the compiler constructs a temporary object that is a conversion of the object passed in. If you remove the const modifier in the CallByReference function declaration, the compiler will complain that it cannot convert an Empty object to a Base reference simply because it is not possible to map Empty onto Base directly… only through implicit conversion.
The Microsoft compiler in this case will complain:
Error C2664: ‘CallByReference’ : cannot convert parameter 1 from ‘Empty’ to ‘Base &’ - A reference that is not to ‘const’ cannot be bound to a non-lvalue.
We can conclude that when a function parameter utilizes a const reference, there is the possibility that an object passed into this function will be implicitly converted. So passing by value might facilitate slicing, which has unpleasant side-effects… but when using const references, we might facilitate the creation and destruction of a temporary variable.
Of course this example using a Base and Empty class is very hypothetical, but if you use the STL you have probably used conversion constructors already.
It is why you can use foo(“some text”) with a function declaration like:
void foo (std::string const &label);
The std::string has a conversion constructor that accepts a char const*… which is very useful; just not free.
It wouldn’t be me writing these articles if I could not find a way to add insult to injury. So how about returning a const reference from a function that was passed to that const reference?
Here is a nasty change to the CallByReference function:
Base const& AnotherCallByReference(Base const &obj);
You should realize now that this function might return a const reference to a temporary object (!). This temporary object is bound to be destroyed (rather) sooner or later… invalidating the reference to it.
As always… not everything is at it may seem to be.
DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.