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