7

Are there any understanding / maintainability issues that result from code like

inVar1 == 0 ? NULL : v.push_back(inVar1);
inVar2 == 0 ? NULL : v.push_back(inVar2);

and so forth.

The possibly confusing idea is using the ternary operator for program flow rather than variable assignment, which is the usual explanation.

I haven't seen coding standards at work that address this usage, so while I'm comfortable doing this I'd like to find out if there is a good reason not to.

user63572
  • 73
  • 1
  • 4
  • 1
    and note that your thing is even wrong in C++. if one operand is void, then the other has to be too, or it has to throw: inVar1 == 0 ? (void)NULL : v.push_back(inVar1); just use a plain if :) – Johannes Schaub - litb Feb 07 '09 at 01:45
  • I have to disagree with "wrong" - VisualStudio 2003 and 2005 allow this. Perhaps we can debate what "wrong" means...? – user63572 Feb 07 '09 at 02:18
  • wrong means when your code is not valid c++, of course. one branch has type void and another has type int, long or so. that's not valid. void and int bite. maybe vc++ has an extension that allows this - i don't know. – Johannes Schaub - litb Feb 07 '09 at 02:43
  • What part of the c++ specification does this violate? I am willing to offer a virtual genuflection in return. – user63572 Feb 07 '09 at 03:06
  • 1
    5.16.2: http://www.kuzbass.ru:8086/docs/isocpp/expr.html#expr.cond – bk1e Feb 07 '09 at 03:25
  • u can now genuflect to bk1e. i will catch it in a photo :p – Johannes Schaub - litb Feb 07 '09 at 03:39
  • Am reading the spec now, and am fervently looking for an escape clause, will admit defeat or spread chaff tomorrow. – user63572 Feb 07 '09 at 03:48
  • There are actually some interesting side effects of this rule (that the two options given the ternary operator must be the same type or convertible to the same type ( http://www.artima.com/cppsource/foreach.html )). – Max Lybbert Feb 07 '09 at 04:38
  • 2
    If you want something more confusing you can try `inVar1 && (v.push_back(inVar1),1);` – Ismael Feb 07 '09 at 13:58
  • bk1e litb Yes it turns out that I was wrong - Visual Studio allows it, but it is not part of the language standard. "just because you can, doesn't mean you should" – user63572 Feb 27 '09 at 04:15
  • possible duplicate of [To ternary or not to ternary?](http://stackoverflow.com/questions/160218/to-ternary-or-not-to-ternary) – om-nom-nom Dec 04 '12 at 21:09

16 Answers16

35

I think it's confusing and a lot harder to read than simply typing;

if (inVar != 0)
  v.push_back(inVar);

I had to scan your example several times to figure out what the result would be with any certainty. I'd even prefer a single-line if() {} statement than your example - and I hate single-line if statements :)

Andrew Grant
  • 58,260
  • 22
  • 130
  • 143
  • I think ed-malabar should judge the readability by determining how much time was _spent_ trying to figure out the code. Semantically, it's dead simple. It shouldn't take me good 30 sec to fully understand it. – Calyth Feb 07 '09 at 01:44
  • 1
    I agree - it sounds like the popular consensus is that more time is spent parsing the code than scrolling. – user63572 Feb 07 '09 at 03:19
25

The ternary operator is meant to return a value.

IMO, it should not mutate state, and the return value should be used.

In the other case, use if statements. If statements are meant to execute code blocs.

thinkbeforecoding
  • 6,668
  • 1
  • 29
  • 31
11

The ternary is a good thing, and I generally promote it's usage.

What you're doing here however tarnishes it's credibility. It's shorter, yes, but it's needlessly complicated.

dicroce
  • 45,396
  • 28
  • 101
  • 140
10

I think this should be avoided. You could use a 1-line if statement in its place.

if(inVar1 != 0) v.push_back(inVar1);
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
  • This is nasty - there is never any good reason to not mark blocks with surrounding bracecs. – mP. Feb 07 '09 at 07:48
  • 3
    @mP: There is: lazyness and sometimes even readability; I often do things like `if(!buffer) return NULL;` to check return values / error conditions, and I find it a lot easier to read if *not* surrounded by braces, independent of what some coding conventions might say... – Christoph Feb 07 '09 at 10:26
  • 5
    @mP: You don't have to just blindly follow coding conventions without *thinking* about why they exist. You don't always need braces around every if block. – Bill the Lizard Feb 07 '09 at 13:50
  • There is no reasonable reason to NOT include the braces ? Its not like typing those 2 characters is so difficult or time consuming. It doesnt add clutter etc and avoids a lot of future problems. – mP. Feb 08 '09 at 00:33
  • @Christoph Yeh until someone adds an extra statement to the the unbraced "block" without thinking and dont add the braces. – mP. Feb 08 '09 at 00:34
  • @mP: When you add another statement, then you can add the braces. When it's all on one line, it's absolutely clear that only that statement is conditional. If you always needed the braces they wouldn't be optional. Besides, some languages don't even have braces. – Bill the Lizard Feb 08 '09 at 01:21
  • @mP: I have seen people make this claim---that someone might add another statement on that line without adding braces first---many, many times, but I have yet to see a single example of it in the wild. – uckelman Oct 11 '10 at 14:04
  • @uckelman: [here's one](http://stackoverflow.com/questions/3786151/tools-to-bracket-conditional-statement) – default Oct 24 '10 at 22:04
6

Compilers these days will make an if as fast as a ternary operator.

You goal should be how easy is it for another software developer to read.

I vote for

if ( inVar != 0 )
{
   v.push_back( inVar );
}

why the brackets...because one day you may want to put something else in there and the brackets are pre-done for you. Most editors these days will put them in anyway.

Gregor Brandt
  • 7,659
  • 38
  • 59
  • 1
    Except in rare cases, I hate the ternary operator. It is hard to read, hard to visually scan for the discrete fields involved, and easy to lose within a larger block of code. – Joe Feb 07 '09 at 02:50
  • 1
    I agree, I avoid it like the plague. – Gregor Brandt Feb 07 '09 at 03:01
  • 1
    I've always thought that reasoning odd (the putting in the brackets right away) - Is it really any harder to put them in later when you decide you need them? – Eclipse Feb 07 '09 at 03:04
  • Did compilers ever make an if-statement slower than a ternary operator? – bk1e Feb 07 '09 at 03:39
  • Yes, and in some cases if-statements are *still* slower than a ternary operator: particularly, those where the compiler can use a conditional-move for a ?: instead of a compare and branch. I ran a test to prove this to myself and in that case (http://tinyurl.com/aaeeqa), tern was 7x faster than if. – Crashworks Feb 07 '09 at 04:15
  • 1
    re Brackets: Its just a pro-active defensive programming style. I do it every time and I have never been hit by accidently forgetting them. I also do it because properly formatted code is FAR easier to read. – Gregor Brandt Feb 07 '09 at 16:39
  • 1
    re Brackets: +1. I was recently debugging some code and noticed somebody's attempt at adding a second indented line to the if statement and without adding brackets. I _always_ add brackets. – Steve Folly Feb 08 '09 at 09:19
  • +1 Just because you put brackets! – xian Mar 08 '09 at 02:08
6

Your use of the ternary operator gains you nothing and you hurt the codes readability.

Since the ternary operator returns a value that you are not using it is odd code. The use of an if is much more clear in a case like yours.

lillq
  • 14,758
  • 20
  • 53
  • 58
5

As litb mentioned in the comments, this isn't valid C++. GCC, for example, will emit an error on this code:

error: `(&v)->std::vector<_Tp, _Alloc>::push_back [with _Tp = int, _Alloc =
std::allocator<int>](((const int&)((const int*)(&inVar1))))' has type `void' 
and is not a throw-expression

However, that can be worked around by casting:

inVar1 == 0 ? (void)0 : v.push_back(inVar1);
inVar2 == 0 ? (void)0 : v.push_back(inVar2);

But at what cost? And for what purpose?

It's not like using the ternary operator here is any more concise than an if-statement in this situation:

inVar1 == 0 ? NULL : v.push_back(inVar1);
if(inVar1 != 0) v.push_back(inVar1);
bk1e
  • 23,871
  • 6
  • 54
  • 65
2

While, in practice, I agree with the sentiments of those who discourage this type of writing (when reading, you have to do extra work to scan the expression for its side effects), I'd like to offer

!inVar1 ?: v.push_back(inVar1);
!inVar2 ?: v.push_back(inVar2);

...if you're going for obscure, that is. GCC allows x ?: y in place of x ? x : y. :-)

ephemient
  • 198,619
  • 38
  • 280
  • 391
2

I use ternary operator when I need to call some function with conditional arguments - in this case it is better then if.

Compare:

printf("%s while executing SQL: %s",
        is_sql_err() ? "Error" : "Warning", sql_msg());

with

if (is_sql_err())
    printf("Error while executing SQL: %s", sql_msg());
else
    printf("Warning while executing SQL: %s", sql_msg());

I find the former is more appealing. And it complies to DRY principle, unlike latter - you don't need to write two nearly identical lines.

qrdl
  • 34,062
  • 14
  • 56
  • 86
  • The ternary conditional operator version also emphasizes the main part of the code -- printing -- rather than the minor part of the code -- whether it's a warning or error. – David Stone Aug 26 '12 at 18:37
1

I think you would be better served in doing a proper if structure. I even prefer to always have braces with my if structures, in the event I have to add lines later to the conditional execution.

if (inVar != 0) {
    v.push_back(inVar);
}
Bill Perkins
  • 194
  • 4
1

I think that sometimes the ternary are a necessary evil in initializer lists for constructors. I use them mostly for constructors where I want to allocate memory and set some pointer to point at it before the body of the constructor.

An example, suppose you had an integer storage class that you wanted to have take a vector as an input but the internal representation is an array:

class foo
{
public:
    foo(std::vector<int> input);
private:
    int* array;
    unsigned int size;
};

foo:foo(std::vector<int> input):size(input.size()), array( (input.size()==0)?
        NULL : new int[input.size])
{
    //code to copy elements and do other start up goes here
}

This is how I use the ternary operator. I don't think it is as confusing as some people do but I do think that one should limit how much they use it.

James Matta
  • 1,562
  • 16
  • 37
1

Most of the tortured ternaries (how's that for alliteration?) I see are merely attempts at putting logic that really belongs in an if statement in a place where an if statement doesn't belong or can't go.

For instance:

if (inVar1 != 0)
  v.push_back(inVar1);
if (inVar2 != 0)
  v.push_back(inVar2);

works assuming that v.push_back is void, but what if it's returning a value that needs to get passed to another function? In that case, it would have to look something like this:

SomeType st;
if (inVar1 != 0)
  st = v.push_back(inVar1);
else if (inVar2 != 0)
  st = v.push_back(inVar2);
SomeFunc(st);

But that's more to digest for such a simple piece of code. My solution: define another function.

SomeType GetST(V v, int inVar1, int inVar2){
    if (inVar1 != 0)
      return v.push_back(inVar1);
    if (inVar2 != 0)
      return v.push_back(inVar2);        
}

//elsewhere
SomeFunc(GetST(V v, inVar1, inVar2));

At any rate, the point is this: if you have some logic that's too tortured for a ternary but will clutter up your code if it's put in an if statement, put it somewhere else!

Jason Baker
  • 192,085
  • 135
  • 376
  • 510
1
inVar1 != 0 || v.push_back(inVar1);
inVar2 != 0 || v.push_back(inVar2);

common pattern found in languages like Perl.

Hafthor
  • 16,358
  • 9
  • 56
  • 65
  • 1
    This is the most initially confusing reworking of my sample code in this thread. But after the initial WTF, it parses. And makes me imagine that Perl developers have really tiny monitors. – user63572 May 21 '09 at 01:35
  • Not a common pattern in languages like C, though. – David Stone Aug 26 '12 at 18:35
0

If you have multiple method invocations in one or both of the tenary arguments then its wrong. All lines of code regardless of what statement should be short and simple, ideally not compounded.

mP.
  • 18,002
  • 10
  • 71
  • 105
0

A proper if statement is more readable, as others have mentioned. Also, when you're stepping through your code with a debugger, you won't be able to readily see which branch of an if is taken when everything is in one line or you're using a ternary expression:

if (cond) doIt();

cond ? noop() : doIt();

Whereas the following is much nicer to step through (whether you have the braces or not):

if (cond) {
    doIt();
}
Ates Goral
  • 137,716
  • 26
  • 137
  • 190
0

As mentioned, it's not shorter or clearer than a 1 line if statement. However, it's also no longer - and isn't really that hard to grok. If you know the ternary operator, it's pretty obvious what's happening.

After all, I don't think anyone would have a problem if it was being assigned to a variable (even if it was mutating state as well):

var2 = inVar1 == 0 ? NULL : v.push_back(inVar1);

The fact that the ternary operator always returns a value - IMO - is irrelevant. There's certainly no requirement that you use all return values...after all, an assignment returns a value.

That being said, I'd replace it with an if statement if I ran across it with a NULL branch.

But, if it replaced a 3 line if statement:

if (inVar == 0) {
   v.doThingOne(1);
} else {
   v.doThingTwo(2);
}

with:

invar1 == 0 ? v.doThingOne(1) : v.doThingTwo(2);

I might leave it...depending on my mood. ;)

Mark Brackett
  • 84,552
  • 17
  • 108
  • 152