Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Most important const" with conditional expression?

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:

  1. If aPtr == 0, is a guaranteed to refer to a valid MyClass object until the end of the function?
  2. If 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.

like image 348
Sneftel Avatar asked May 27 '15 17:05

Sneftel


3 Answers

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.

like image 182
Kerrek SB Avatar answered Nov 15 '22 13:11

Kerrek SB


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.

like image 23
Barry Avatar answered Nov 15 '22 14:11

Barry


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.

like image 28
Christophe Avatar answered Nov 15 '22 13:11

Christophe