13

I am baffled by this:

#include <iostream>
struct X {};
void f( const X &x ) { std::cerr << &x << "\n"; }

static X x;
int main()
{
  f( x ); // Off stack address
  f( false ? X() : x ); // Different address on stack.
}

Why would the second invocation of f make a temporary copy?

Edit: This question is not so much about the type X, but the fact that a copy is made. I was missing the sublety of value categories from the accepted answer and had expected f's parameter to bind on either x or X() directly like it does when rephrasing this as an if statement.

bking
  • 275
  • 2
  • 11
  • 2
    See http://stackoverflow.com/questions/8535226/return-type-of-ternary-conditional-operator – Michael Price May 06 '15 at 21:35
  • 2
    See: http://stackoverflow.com/questions/8535226/return-type-of-ternary-conditional-operator – Owbea May 06 '15 at 21:35
  • 1
    The linked question was about having different types on both sides of the colon. While the answers discuss value categories, they would not explain directly why copy construction is necessary in the question that I have asked. I have edited my question to clarify that. – bking May 07 '15 at 07:12

1 Answers1

13

The conditional operator determines a common type and value category for its second and third operands. Whichever branch is chosen (as determined by the condition), the corresponding operand is converted into this common type and value category.

In this case X() and x both have type X, so the common type is of course X itself. But the result is a prvalue, so if x is chosen (the condition is false) then the lvalue-to-rvalue conversion is applied, creating a prvalue temporary copy of x which is then bound to the reference.

In conclusion, the use of the conditional operator itself is what forces the copy to be made.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Justification in standard [*§5.16 Conditional operator*]. – 101010 May 06 '15 at 21:44
  • I was using C++03 in that case. Could you give an explanation on why the copy is necessary there? Wouldn't it be possible in priciple for the compiler to do a kind of 'cast on value category' instead? – bking May 07 '15 at 07:19
  • @bking C++03 still has lvalues and rvalues. Just replace "prvalue" with "rvalue" in my answer. – Brian Bi May 07 '15 at 07:22
  • http://stackoverflow.com/a/20999389/3766665 explains the copying during the lvalue to rvalue conversion in a little more detail. – bking May 07 '15 at 10:43