Benefits of Using the Conditional : (Ternary) Operator

Benefits of using the conditional ?: (ternary) operator

I would basically recommend using it only when the resulting statement is extremely short and represents a significant increase in conciseness over the if/else equivalent without sacrificing readability.

Good example:

int result = Check() ? 1 : 0;

Bad example:

int result = FirstCheck() ? 1 : SecondCheck() ? 1 : ThirdCheck() ? 1 : 0;

Benefits of ternary operator vs. if statement

Performance

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..

Things you can only do with ? :

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) { }

Factoring for concise code

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);

Can't named temporaries improve the if/else monstrosity above?

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.

Functional difference: unifying result type

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.

Emphasis: value-selection before/after action needing values

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".

advantage of ternary operator over if else?

If you look at the disassembly of both approaches, they're generally the same on any modern compiler I know of. The ternary operator is just a compact form of writing the same thing.

Here's an example using gcc 4.2.1 on Mac OS X:

With if/else:

int x = 1;
int y = 2;
int z;

if (x < y)
{
z = 3;
}
else
{
z = 4;
}

With the ternary operator:

int x = 1;
int y = 2;
int z = (x < y) ? 3 : 4;

If you run gcc -S test.c on both of these, you get this assembly for the if/else version:

    movl    $1, -16(%rbp)
movl $2, -20(%rbp)
movl -16(%rbp), %eax
movl -20(%rbp), %ecx
cmpl %ecx, %eax
jge LBB1_2
movl $3, -12(%rbp)
jmp LBB1_3
LBB1_2:
movl $4, -12(%rbp)

and this for the ternary operator version:

    movl    $1, -12(%rbp)
movl $2, -16(%rbp)
movl -12(%rbp), %eax
movl -16(%rbp), %ecx
cmpl %ecx, %eax
jge LBB1_2
movl $3, -20(%rbp)
jmp LBB1_3
LBB1_2:
movl $4, -20(%rbp)

The register offsets are different, but functionally, the code does the same thing. It adds two literals to two different registers, then compares and jumps based on the result of the comparison.

C# - Are there any advantages or benefits to using ternary operators?

The ternary operator is more concise: less typing and quicker to read. The compiled IL code or the JITted native code are likely to be identical. If not, any performance differences are almost certain to be virtually unmeasurably small. Therefore, source code quality is the only real consideration in making the decision.

"Tertiary" means "third in importance"; "ternary" means "having three parts."

I personally prefer the term "conditional operator" because it's entirely possible that someone will invent another ternary operator in the future. Imagine if we had to call + the "first binary operator" and - the "second binary operator"!

Why would you choose to use the ternary operator (?:) over a traditional if..else block?

As you say, is a compressed if-then-else, so it must be compiled to the same instructions with regards of the comparisons.

The real difference is that it selects a final result. Really, just syntactic sugar that improves readability.

Compare:

int val = (foobar == true) ? 500 : 1000 ;

vs:

int val = 0;
if (foobar == true)
{
val = 500;
} else {
val = 1000;
}

It can be argued that it is just a matter of style, thus subjective. But, the legibility becomes even greater when you have a more complex decision tree, such as:

int val = (flag01 == true) ? 
(flag02 == true ) ? 100 : 200 :
(flag03 == true ) ? 300 : 400 ;

You can chain together many decisions in one place instead of writing a bunch of nested if-then-else clauses:

 int val = 0;
if (flag01 == true)
{
if (flag02 == true)
{
val = 100;
} else {
val = 200;
}
} else {
if (flag03 == true)
{
val = 300;
} else {
val = 400;
}
}

This is somewhat following a code pattern called method chaining.

To ternary or not to ternary?

Use it for simple expressions only:

int a = (b > 10) ? c : d;

Don't chain or nest ternary operators as it hard to read and confusing:

int a = b > 10 ? c < 20 ? 50 : 80 : e == 2 ? 4 : 8;

Moreover, when using ternary operator, consider formatting the code in a way that improve readability:

int a = (b > 10) ? some_value                 
: another_value;

Ternary ? operator vs the conventional If-else operator in c#

I ran 100 million Ternary Operators and 100 million If-Else statements and recorded the performance of each. Here is the code:

Stopwatch s = new Stopwatch();
// System.Diagnostics Stopwatch
int test = 0;
s.Start();
for(int a = 0; a < 100000000; a++)
test = a % 50 == 0 ? 1 : 2;
s.Stop();

s.Restart();
for(int b = 0; b < 100000000; b++)
{
if(b % 50 == 0)
test = 1;
else
test = 2;
}
s.Stop();

Here is the results (ran on an Intel Atom 1.66ghz with 1gb ram and I know, it sucks):

  • Ternary Operator: 5986 milliseconds or 0.00000005986 seconds per each operator.

  • If-Else: 5667 milliseconds or 0.00000005667 seconds per each statement.

Don't forget that I ran 100 million of them, and I don't think 0.00000000319 seconds difference between the two matters that much.

Full if/else statement vs. Conditional Operator

Don't focus on writing less code... focus on the code you end up with being more readable.

Sometimes an if statement will be more readable. Sometimes the conditional operator will be more readable. I like the conditional operator in cases where it makes sense, in terms of different ways of calculating one logical value (e.g. a discount, based on the age of a customer). I don't like using it in convoluted ways though - there's nothing wrong with using a full if/else where it makes sense.

It's worth remembering the null-coalescing operator too... so instead of:

string shipTo = customer.ShippingAddress != null 
? customer.ShippingAddress : customer.BillingAddress;

you can use

string shipTo = customer.ShippingAddress ?? customer.BillingAddress;

Again, it's only useful in certain situations - but in those cases it's really handy.



Related Topics



Leave a reply



Submit