I'm browsing through some code and I found a few ternary operators in it. This code is a library that we use, and it's supposed to be quite fast.
I'm thinking if we're saving anything except for space there.
What's your experience?
Yes! The second is vastly more readable.
An if / else statement emphasises the branching first and what's to be done is secondary, while a ternary operator emphasises what's to be done over the selection of the values to do it with.
If condition is preferred in case if program requires executing only on true block. In this case, it is necessary to work around to use Ternary Operator. Nested Ternary Operator is not readable and can not be debugged easily. If else is more readable and easier to debug in case of issue.
The only "advantage" is that you can use the ternary operator in an expression (eg. function arguments), making for terser code. using an if , you'd duplicate the full expression.
The ternary operator shouldn't differ in performance from a well-written equivalent if
/else
statement... they may well resolve to the same representation in the Abstract Syntax Tree, undergo the same optimisations etc..
If you're initialising a constant or reference, or working out which value to use inside a member initialisation list, then if
/else
statements can't be used but ?
:
can be:
const int x = f() ? 10 : 2; X::X() : n_(n > 0 ? 2 * n : 0) { }
Keys reasons to use ?
:
include localisation, and avoiding redundantly repeating other parts of the same statements/function-calls, for example:
if (condition) return x; else return y;
...is only preferable to...
return condition ? x : y;
...on readability grounds if dealing with very inexperienced programmers, or some of the terms are complicated enough that the ?
:
structure gets lost in the noise. In more complex cases like:
fn(condition1 ? t1 : f1, condition2 ? t2 : f2, condition3 ? t3 : f3);
An equivalent if
/else
:
if (condition1) if (condition2) if (condition3) fn(t1, t2, t3); else fn(t1, t2, f3); else if (condition3) fn(t1, f2, t3); else fn(t1, f2, f3); else if (condition2) ...etc...
That's a lot of extra function calls that the compiler may or may not optimise away.
Further, ?
allows you to select an object, then use a member thereof:
(f() ? a : b).fn(g() ? c : d).field_name);
The equivalent if
/else
would be:
if (f()) if (g()) x.fn(c.field_name); else x.fn(d.field_name); else if (g()) y.fn(c.field_name); else y.fn(d.field_name);
If the expressions t1
, f1
, t2
etc. are too verbose to type repeatedly, creating named temporaries may help, but then:
To get performance matching ?
:
you may need to use std::move
, except when the same temporary is passed to two &&
parameters in the function called: then you must avoid it. That's more complex and error-prone.
c ?
x :
y evaluates c then either but not both of x and y, which makes it safe to say test a pointer isn't nullptr
before using it, while providing some fallback value/behaviour. The code only gets the side effects of whichever of x and y is actually selected. With named temporaries, you may need if
/ else
around or ?
:
inside their initialisation to prevent unwanted code executing, or code executing more often than desired.
Consider:
void is(int) { std::cout << "int\n"; } void is(double) { std::cout << "double\n"; } void f(bool expr) { is(expr ? 1 : 2.0); if (expr) is(1); else is(2.0); }
In the conditional operator version above, 1
undergoes a Standard Conversion to double
so that the type matched 2.0
, meaning the is(double)
overload is called even for the true
/1
situation. The if
/else
statement doesn't trigger this conversion: the true
/1
branch calls is(int)
.
You can't use expressions with an overall type of void
in a conditional operator either, whereas they're valid in statements under an if
/else
.
There's a different emphasis:
An if
/else
statement emphasises the branching first and what's to be done is secondary, while a ternary operator emphasises what's to be done over the selection of the values to do it with.
In different situations, either may better reflect the programmer's "natural" perspective on the code and make it easier to understand, verify and maintain. You may find yourself selecting one over the other based on the order in which you consider these factors when writing the code - if you've launched into "doing something" then find you might use one of a couple (or few) values to do it with, ?
:
is the least disruptive way to express that and continue your coding "flow".
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