c++copy-constructorconditional-operator

Why does a copy occur in ternary statement?


I can't quite figure out why a copy is occurring here. Consider the following:

#include <iostream>
struct X {
    X() = default;
    X(int value) : value(value) {}
    X(const X &x) : value(x.value) {
        std::cout << "copy" << std::endl;    
    }
    int value = 0;  
};

struct Z {
    Z(const X &x) : x(x) {}
    const X &get_x() const {return x;}
    X x;    
};

X get_x(const Z &z) {
    return z.get_x();
}

int main() {
    X x1(2);
    Z z1(x1);
    std::cout << "----" << std::endl;
    const X &x2 = (true) ? z1.get_x() : get_x(z1);    // (1)
    std::cout << "----" << std::endl;
    const X &x3 = (true) ? z1.get_x() : z1.get_x();   // (2)
}

The output is:

copy
----
copy
----

Why does a copy occur when the result of the false statement changes from a function returning a copy vs a function returning a const-reference, when both should take the true condition? I would have thought that in both (1) and (2), whatever is in the false condition should not have a side effect.


Solution

  • The type, and more importantly here the value category of ? : has to be determined at compile-time.

    If both operands are lvalues, then the result is also an lvalue, and all is well (no extra copy).

    But if one operand is a prvalue (aka a temporary object), then the whole ? : must also return a prvalue. So if the branch taken is originally not a prvalue, a temporary object must be constructed to get one, hence a copy.


    Note that I'm intentionally wording this in terms of value categories, rather than reference-ness of types, because expressions can't have refernece types.