Is it OK to explicitly cast prvalue to const reference to avoid extra copy in ternary operator?

156 Views Asked by At

Let's suppose we have some non-copyable and non-movable type Foo, and a function

int f(const Foo& foo) {
  ... // somehow compute the result depending on the value of foo.
}

Now let's suppose we want want to call this function like this:

const Foo foo1{ 42 };
bool condition = getCondition();
int result = f(condition ? foo : Foo{ 150 });

This won't compile because foo and Foo{ 150 } are of different value categories, so, by definition, the ternary operator will return a prvalue of type Foo - which means that foo1 will need to be copied if condition == true, so this causes a compilation error.

However, C++ guarantees that temporary objects remain valid until the end of the expression, so we can instead write this:

const Foo foo1{ 42 };
bool condition = getCondition();
int result = f(condition ? foo1 : static_cast<const Foo&>(Foo{ 150 });

Now the ternary operator returns const Foo&, and even if this reference refers to Foo{ 150 }, this temporary object will remain valid until the end of the expression. No copying/moving takes place, so this compiles.

Obviously, a safer option would be to just write:

int result = condition ? f(foo1) : f(Foo{ 150 });

However, this can lead to a lot of boilerplate if there are additional parameters, and grows exponentially for multiple "conditional" parameters:

int result = condition1
               ? (condition2 ? f(foo1, foo2) : f(foo1, Foo{ 222 }))
               : (condition2 ? f(Foo{ 111 }, foo2) : f(Foo{ 111 }, Foo{ 222 }));

The hack above allows a shorter form:

int result = f( condition1 ? foo1 : static_cast<const Foo&>(Foo{ 111 }),
                condition2 ? foo2 : static_cast<const Foo&>(Foo{ 222 }) );

I understand that this is somewhat risk-prone, but am I correct that the code above is valid?

P.S. I also wonder whether the compiler generates O(N) or O(2^N) instructions when there are N such "conditional" parameters.

Ternary operator and prolonging the lifetime of a temporary object via reference-to-const addresses a similar scenario, but the question there is whether the ternary operator itself prolongs the lifetime of a temporary - it doesn't.

0

There are 0 best solutions below