Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Safe to return const & to object passed by const &?

When compiling the following code with clang-3.5 to the C++11 standard:

const String& XMLAttributes::getValueAsString(const String& attrName, const String& def) const
{
    return (exists(attrName)) ? getValue(attrName) : def;
}

I get the following warning:

warning: 
  returning reference to local temporary object [-Wreturn-stack-address]
    return (exists(attrName)) ? getValue(attrName) : def;
                                                     ^~~
note: 
  binding reference variable 'def' here
  ...String& attrName, const String& def) const
                                 ^
1 warning generated.

g++-4.9 does not give a similar warning.

My belief is that clang is being overzealous and that it should work fine in this case, since I know that when this function is used the input has a long enough lifetime. (I'm pretty sure I've seen a good amount of code that seems to work this way anyways...)

I'm a little bit spooked by the fact that clang says its a "local temporary object" though. Really nothing there should be a local temporary object, and if clang thinks so and is deleting things while this function is running I would like to know why.

Does the standard guarantee that the reference returned by this function (in the case where "def" is selected) will have the same lifetime as the reference that is passed in, or does it allow that they can be thought of as two different references with different lifetimes?

like image 402
Chris Beck Avatar asked Jan 09 '23 07:01

Chris Beck


2 Answers

No, it's not safe.

The ternary conditional operator evaluates to a copy of its operands, in this case. Hence the warning.

You could probably work around it by injecting a nice little std::ref.

[C++11: 5.16/6]: The second and third operands have the same type; the result is of that type. If the operands have class type, the result is a prvalue temporary of the result type, which is copy-initialized from either the second operand or the third operand depending on the value of the first operand.

like image 54
Lightness Races in Orbit Avatar answered Jan 18 '23 09:01

Lightness Races in Orbit


def is not a temporary object here. If getValue actually returns const String& then the conditional operator does not generate a temporary object either.

However this function design is dangerous because there is no protection against bugs like this:

const String &bar = getValueAsString( "bla", "" );   // oops

The temporary string created from "" dies when getValueAsString returns, so bar is now a dangling reference (if the default was triggered, of course).

The lifetime extension only occurs when a temporary is directly bound to a reference. Initializing other references from that reference does not re-extend the temporary's lifetime.

Possibly clang actually detected that your code does contain this bug, i.e. you bound a temporary to def when calling this function at some point.

like image 38
M.M Avatar answered Jan 18 '23 10:01

M.M