Consider the following code:
int foo(MyClass const* aPtr = 0) {
MyClass const& a = aPtr ? *aPtr : MyClass(); // Either bind to *aPtr, or to a default-constructed MyClass
...
return a.bar();
}
The "most important const" is, hopefully, being used here. The aim is to allow for a null aPtr
to be passed in (BTW, yes, it must be a pointer argument), in which case a temporary MyClass
object would be default-constructed, and its lifetime extended by a const reference binding to it. Whereas, if aPtr
was not null, the reference would bind to its pointed-to object without any (expensive) copy construction happening.
The questions two are:
aPtr == 0
, is a
guaranteed to refer to a valid MyClass
object until the end of the function?aPtr != 0
, will a
bind to it, rather than to some other MyClass
?Based on testing, the answer to 1 is almost certainly "yes". #2 I'm not so sure about, though (copy elision and such)... it seems possible that the conditional expression would end up copy-constructing a temporary MyClass
from *aPtr
, and extending the life of that temporary.
Your conditional expression is a prvalue (because one of its operands is). If the first alternative of the conditional operator is selected, it is converted to a temporary (which incurs a copy). That temporary is bound to the reference, and the usual life-time extension applies.
The relevant standardese [expr.cond]:
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.
To the first, yes a
is guaranteed to refer to a valid MyClass
object. This comes directly from [class.temporary]/4-5:
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when a default constructor is called [...]
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
- A temporary bound to a reference member in a constructor’s ctor-initializer [...]
- A temporary bound to a reference parameter in a function call [...]
- The lifetime of a temporary bound to the returned value in a function return statement [...]
- A temporary bound to a reference in a new-initializer [...]
None of those exceptions apply.
If aPtr
is a valid pointer, then a copy is made because the type of aPtr ? *aPtr : MyClass{}
is just MyClass
. That temporary gets bound to a
, and its lifetime also persists for the same reason.
About 1) See kerek's answer above
About 2) The standard says about conditional operator:
5.16/4: If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category (...).
5.16/5: Otherwise, the result is a prvalue. (...)
According to the taxonomy of lvalues and rvalues in 3.10/1, *aPtr
is an lvalue , MyClass()
is an prvalue . Hence the result should be a prvalue so that the reference should refer to this temporary (potentially a copy constructed temp).
Edit: Here an online demo, which shows that the const reference refers to a temporary and not to the original object pointed to by aPtr.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With