Why Switch/Case and not If/Else If? - c++

This question in mainly pointed at C/C++, but I guess other languages are relevant as well.
I can't understand why is switch/case still being used instead of if/else if. It seems to me much like using goto's, and results in the same sort of messy code, while the same results could be acheived with if/else if's in a much more organized manner.
Still, I see these blocks around quite often. A common place to find them is near a message-loop (WndProc...), whereas these are among the places when they raise the heaviest havoc: variables are shared along the entire block, even when not propriate (and can't be initialized inside it). Extra attention has to be put on not dropping break's, and so on...
Personally, I avoid using them, and I wonder wether I'm missing something?
Are they more efficient than if/else's?
Are they carried on by tradition?

Summarising my initial post and comments - there are several advantages of switch statement over if/else statement:
Cleaner code. Code with multiple chained if/else if ... looks messy and is difficult to maintain - switch gives cleaner structure.
Performance. For dense case values compiler generates jump table, for sparse - binary search or series of if/else, so in worst case switch is as fast as if/else, but typically faster. Although some compilers can similarly optimise if/else.
Test order doesn't matter. To speed up series of if/else tests one needs to put more likely cases first. With switch/case programmer doesn't need to think about this.
Default can be anywhere. With if/else default case must be at the very end - after last else. In switch - default can be anywhere, wherever programmer finds it more appropriate.
Common code. If you need to execute common code for several cases, you may omit break and the execution will "fall through" - something you cannot achieve with if/else. (There is a good practice to place a special comment /* FALLTHROUGH */ for such cases - lint recognises it and doesn't complain, without this comment it does complain as it is common error to forgot break).
Thanks to all commenters.

Well, one reason is clarity....
if you have a switch/case, then the expression can't change....
i.e.
switch (foo[bar][baz]) {
case 'a':
...
break;
case 'b':
...
break;
}
whereas with if/else, if you write by mistake (or intent):
if (foo[bar][baz] == 'a') {
....
}
else if (foo[bar][baz+1] == 'b') {
....
}
people reading your code will wonder "were the foo expressions supposed to be the same", or "why are they different"?

please remember that case/select provides additional flexibility:
condition is evaluated once
is flexible enough to build things like the Duff's device
fallthrough (aka case without break)
as well as it executes much faster (via jump/lookup table) * historically

Also remember that switch statements allows the flow of control to continue, which allows you to nicely combine conditions while allowing you to add additional code for certain conditions, such as in the following piece of code:
switch (dayOfWeek)
{
case MONDAY:
garfieldUnhappy = true;
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
weekDay = true;
break;
case SATURDAY:
weekendJustStarted = true;
case SUNDAY:
weekendDay = true;
break;
}
Using if/else statements here instead would not be anywhere as nice.
if (dayOfWeek == MONDAY)
{
garfieldUnhappy = true;
}
if (dayOfWeek == SATURDAY)
{
weekendJustStarted = true;
}
if (dayOfWeek == MONDAY || dayOfWeek == TUESDAY || dayOfWeek == WEDNESDAY
|| dayOfWeek == THURSDAY || dayOfWeek == FRIDAY)
{
weekDay = true;
}
else if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY)
{
weekendDay = true;
}

If there are lots of cases, the switch statement seems cleaner.
It's also nice when you have multiple values for which you want the same behavior - just using multiple "case" statements that fall through to a single implementation is much easier to read than a if( this || that || someotherthing || ... )

It might also depend on your language -- For example, some languages switch only works with numeric types, so it saves you some typing when you're working with an enumerated value, numeric constants... etc...
If (day == DAYOFWEEK_MONDAY) {
//...
}
else if (day == DAYOFWEEK_TUESDAY) {
//...
}
//etc...
Or slightly easier to read...
switch (day) {
case DAYOFWEEK_MONDAY :
//...
case DAYOFWEEK_TUESDAY :
//...
//etc...
}

Switch/case is usually optimized more efficiently than if/else if/else, but is occasionally (depending on language and compiler) translated to simple if/else if/else statements.
I personally think switch statements makes code more readable than a bunch of if statements; provided that you follow a few simple rules. Rules you should probably follow even for your if/else if/else situations, but that's again my opinion.
Those rules:
Never, ever, have more than one line on your switch block. Call a method or function and do your work there.
Always check for break/ case fallthrough.
Bubble up exceptions.

Clarity. As I said here, a clue that else if is problematic is
the frequency with which ELSE IF is
used in a far more constrained way
than is allowed by the syntax. It is a
sledgehammer of flexibility,
permitting entirely unrelated
conditions to be tested. But it is
routinely used to swat the flies of
CASE, comparing the same expression
with alternate values...
This reduces the readability of the
code. Since the structure permits a
universe of conditional complexity,
the reader needs to keep more
possibilities in mind when parsing
ELSE IF than when parsing CASE.

Actually a switch statement implies that you are working off of something that is more or less an enum which gives you an instant clue what's going on.
That said, a switch on an enum in any OO language could probably be coded better--and a series of if/else's on the same "enum" style value would be at least as bad and even worse at conveying meaning.

addressing the concern that everything inside the switch has equivalent scope, you can always throw your case logic into another { } block, like so ..
switch( thing ) {
case ONETHING: {
int x; // local to the case!
...
}
break;
case ANOTHERTHING: {
int x; // a different x than the other one
}
break;
}
.. now I'm not saying that's pretty. Just putting it out there as something that's possible if you absolutely have to isolate something in one case from another.
one other thought on the scope issue - it seems like a good practice to only put one switch inside a function, and not a lot else. Under those circumstances, variable scope isn't as much of a concern, since that way you're generally only dealing with one case of execution on any given invocation of the function.
ok, one last thought on switches: if a function contains more than a couple of switches, it's probably time to refactor your code. If a function contains nested switches, it's probably a clue to rethink your design a bit =)

switch case is mainly used to have the choice to made in the programming .This is not related the conditional statement as :
if your program only require the choice to make then why you use the if/else block and increase the programming effort plus it reduce the execution speed of the program .

Switch statements can be optimized for speed, but can take up more memory if the case values are spread out over large numbers of values.
if/else are generally slow, as each value needs to be checked.

A Smalltalker might reject both switch and if-then-else's and might write something like:-
shortToLongDaysMap := Dictionary new.
shortToLongDaysMap
at: 'Mon' put: 'Monday';
at: 'Tue' put: 'Tuesday';
at: 'Wed' put: 'Wednesday'
etc etc.
longForm := shortToLongDaysMap at: shortForm ifAbsent: [shortForm]
This is a trivial example but I hope you can see how this technique scales for large numbers of cases.
Note the second argument to at:IfAbsent: is similar to the default clause of a case statement.

The main reason behind this is Maintainability and readability. Its easy to make code more readable and maintainable with Switch/case statement then if/else. Because you have many if/else then code become so much messy like nest and its very hard to maintain it.
And some how execution time is another reason.

Pretty sure they compile to the same things as if/else if, but I find the switch/case easier to read when there are more than 2 or 3 elses.

Related

standard alternative for non-standard gnu case ranges

I have a question for a quick workaround to enjoy the benefits of the non-standard gnu case ranges. For example, the non-standard:
case 1 ... 5:
Could be replaced by:
case 1:
case 2:
case 3:
case 4:
case 5:
Probably some macro solution might be in order. From my memory macro loops cannot loop for large numbers of iterations. For this reason, what if the range is "large", say in the thousands?
If you're talking preprocessor loops I guess that you're thinking of the preprocessor meta programming from boost. While it's probably quite portable, the loops seems to be limited to 255 "iterations". In fact the implementation is not a real loop, it's more like a hard coded loop-unroll (thereby the limitation). You could of course expand this to more iterations.
While the preprocessor trick could be tempting, I think you should consider using if-else if-else construct. What's actually (often) happens in a modern compiler regarding conditionals is that it boils down to the same construct that should generate the same code (unless you trick the compiler into evaluating the variable multiple times).
You could even combine the constructs, using a switch-case construct for all singular alternatives and then after the default label add an if-else if-else to handle all ranges.
A third solution would be to write a script that finds the case-ranges and replace them by a standard construct, this should be fairly straight forward in most cases as case can't appear in many places without being a keyword and then it should be followed by an expression which can't contain ... in that way. The only problematic situation (that I can think of) would be when the case-range is a result of preprocessor expansion.
The best alternative would be to re-factor the code to use if/else. If there truly are thousands of cases it may or may not be very efficient to have a giant case statement in the first place.
However, it because cases could "fall-through" or other odd flow control like Duff's device (I hope not), it may not be completely a straightforward conversion.
It is not likely to be a very good implementation to abuse the preprocessor to "loop". See Writing a while loop in the C preprocessor for sample of what this might look like.
It may be best to write a simple python or awk script. However this approach may also be flawed if the keyword case appears somewhere like a string or if labels the preprocessor changes anything. This may work very well for a narrow one-off conversion though, but without seeing the code in question it is hard to say.
There is a serious problem with either preprocessing approach if the case labels use enumerations. Since enums are still just text strings at the time the preprocessor runs (or an external script), how can it iterate from STATE_10 to STATE_20 without knowing what integers they represent? It can't - the GNU extension really requires compiler support.
If a one-time wholesale replacement of the case statement is too invasive or irregular to manage, you could probably utilize a hybrid approach:
Assuming you have a (notional) example like:
switch(state)
{
case STATE_1:
xxx; break;
case STATE_2 ... STATE_10:
yyy; break;
}
Allocate a previously unused range of indexes. Add one new special index for each existing range label. Use if/else logic to detect the ranges first and then replace the range case with a new standard one. This allows the control flow structure to remain essentially unmodified:
#if !defined(__GNUC__)
#define STATE_RANGE_2_10 101
if(state >= 2 && state <= 10)
state2 = STATE_RANGE_2_10
else if(...)
state2 = STATE_RANGE_x_y
else
state2 = state;
#else /* GNU */
#define STATE_RANGE_2_10 STATE_2 ... STATE_10
state2 = state;
#endif
switch(state2)
{
case STATE_1:
xxx; break;
case STATE_RANGE_2_10:
yyy; break;
}
With some suitable macros this could even be made portable between GNUC and real C if you really wanted GNUC to still use the extension for some reason. Note: I introduced the state2 variable in case it is stored or used outside the local scope. If not, you can skip that.

if/else coding style consequences

I am a novice programmer and was in lecture one evening, we were studying the "if,else" coding section from my professor and I was curious about an aspect of it. What I was curious about was if we have a bunch of nested if,else's in our program, is it just bad coding style to end an if,else with an "else,if" line of code instead of if "x", else "y"? For example,
if "x"
else if "y"
else if "z"
end
compared to
if "x"
else if "y"
else "z"
end
It would still run the program without an error, but are there consequences later on other than having bad programming style?
Behind the curtain JS dont really have else if, all it is doing is generating another if statement when parsed.
e.g:
if(foo){
} else if (baz){
}
becomes
if (foo){
} else {
if (baz){
}
}
So the reason for using another else if in the end instead of else is when you want to control the else statement as-well and not just pass to that case everything else that don't fit in your first condition... (In order to control the else condition and filter it to the necessary items only)
if you do have a really long statement with a lot of else-if conditions you should consider using switch statement instead.
It all depends on what you are looking to do. The former example makes sure that all IF requirements are met. There would be instances that none of the IFs get hit in this case.
In the latter example however, ELSE "Z" would get hit for sure if all above IFs fail. This would be useful if you are assigning a variable within your IFs - your variable will definitely have a value at the end of the IF statement. If it was as in the first example, the variable will be null and might result in a null error if you try to use it later.
If there are a lot of if-thens, I would checkout the case/switch statement as well, as it is more neater to implement.
Also, remember to comment your code well - especially explaining what all the nested IFs are doing.

Is it preferred to use else or else-if for the final branch of a conditional

What's preferred
if n > 0
# do something
elsif n == 0
# do something
elsif n < 0
# do something
end
or
if n > 0
# do something
elsif n == 0
# do something
else
# do something
end
I was using else for awhile. I recently switched to doing elsif. My conclusions are the first option adds readability at the cost of more typing, but could confuse some people if they expect an else. I don't have enough experience to know if the first option would create more readability or more confusion and if there are other pros/cons I've missed.
For my specific example, where the scope of else is comparable to the previous conditions and what it does catch can be expressed as a simple condition, is using else preferable for the final branch? Also, would a different conditional influence the answer? I wrote the code in Ruby but I assume the answer is language agnostic. If it isn't, I would also like to know why.
You should know every execution path through your conditional logic regardless. That being said, a simple else for the remaining case is usually the most clear. If you only have an else if it makes the next guy scratch his head and wonder if you are missing something. If it makes you feel better to say "else what" then put an inline comment in else # n < 0
edit after your edit:
For my specific example, where the scope of else is comparable to the previous conditions and what it does catch can be expressed as a simple condition, is using else preferable for the final branch? Also, would a different conditional influence the answer? I wrote the code in Ruby but I assume the answer is language agnostic. If it isn't, I would also like to know why.
Yes, else is still preferable. I can't think of any scenario that changes this answer. Having the else implies completeness in your logic. Not having the else will be distracting - others (or you at a later date) will have to spend more time scrutinizing the code to double check to make sure all conditions are being handled. In fact the coding standard I abide by, the Embedded C Coding Standard by BARR Group says this:
"Any if statement with an else if clause shall end with an else clause. ... This is the equivalent of requiring a default case in every switch."
This echos MISRA rule 14.10
All if ... else if constructs shall be terminated with an else clause.
*All of my examples pertain to C, but as you said in the post, this is someone agnostic to the language being used.
I always prefer the plain else - it makes it obvious to anyone that your intention is "if all else fails, do this" (no pun intended).
In your example you only have 3 possible states, but what about if there were more?
If you want the final conditional statement to be a catch-all, then you should use just else. If you would like, for clarity, you could do something like this:
else # n < 0
# do something
(Note the n < 0 is just a comment). A couple reasons for this:
Depending on the language/compiler, there could be a speed improvement
Less likely to make a programming mistake. Your example above is fairly simple, but imagine a situation in which there is a fourth option that you did not consider at the time of programming? In that case, no action would be taken, when you probably meant for it to fall into the else statement.
else creates a catch-all, with only elsifs none of the choices might be executed. Each specific situation needs its own solution, so I would use what is necessary.
else clarifies that it is the only branch of the program at that point. If the execution makes it there, no matter what it should go through the else. If that's your intention, use else, not elseif because although you can make the exact same program using elseif as the last statement, it sort of linguistically implies that it will only conditionally execute that block of code.

Any reason to replace while(condition) with for(;condition;) in C++?

Looks like
while( condition ) {
//do stuff
}
is completely equivalent to
for( ; condition; ) {
//do stuff
}
Is there any reason to use the latter instead of the former?
There's no good reason as far as I know. You're intentionally misleading people by using a for-loop that doesn't increment anything.
Update:
Based on the OP's comment to the question, I can speculate on how you might see such a construct in real code. I've seen (and used) this before:
lots::of::namespaces::container::iterator iter = foo.begin();
for (; iter != foo.end(); ++iter)
{
// do stuff
}
But that's as far as I'll go with leaving things out of a for-loop. Perhaps your project had a loop that looked like that at one time. If you add code that removes elements of a container in the middle of the loop, you likely have to control carefully how iter is incremented. That could lead to code that looks like this:
for (; iter != foo.end(); )
{
// do stuff
if (condition)
{
iter = foo.erase(iter);
}
else
{
++iter;
}
}
However, that's no excuse for not taking the five seconds needed to change it into a while-loop.
Some compilers warn about constant loop conditions:
while (true) { /* ... */ } /* Warning! */
for (;;) { /* ... */ } /* No warning */
In the specific case of an infinite loop, I might choose a for loop over a while loop for that reason. But if the condition is not empty, I don't really see any benefit. My guess as to why it appeared in the mentioned project is that the code somehow evolved through maintenance, but was written in a more conventional way originally.
No. No. No.
Even if there were a microscopic performance difference, you'd have to be an end-stage Jedi performance tuner to have it matter enough to care.
Is there any reason to use the latter
instead of the former?
A misguided effort to impress your colleagues that you know that those two forms are equivalent.
A foolish maneuver to ensure "job security" by making your code as confusing as possible so that no one will ever want to change it.
The "w" key on your keyboard is broken.
It started life as a for loop with initializers and incrementing condition, and when the logic changed, the developer was too busy to change it.
It's possible to compile
for(INIT; CONDITION; UPDATE)
{
BODY
}
into
{
INIT
while(CONDITION)
{
BODY
UPDATE
}
}
UPDATE: The seemingly redundant extra scope is to cage any variable definitions in INIT, i.e. from for(int i = 0; ...). Thanks!
It's basically just a reordering of the expressions. So there's no reason to prefer one over the other, for performance reasons. I would recommend while() if possible, since it's simpler. If a simpler construct expresses what you want to do, I think that's the one to use.
As far as I know the two statements are optimized by the compiler into the same assember code anyway.. so no, there's no reason to do so - just personal preference.
I think "while" and "for" loops are meant for different idioms. The idiom of using "while" is "do something, while certain conditions are true". The idiom for "for" is "iterate over a certain range of elements"...
Whenever I read a code, I expect these idioms (and I think I am not alone). When I see "for" I understand, that someone is iterating over the certain range and I do not go into details. When I see the for cycle, used for another idiom (not the one, I expect), I get confused and have to go into details.
Anyway, it is very subjective...
In this case, I personally prefer the first loop as it is easier to write and read.
But if I have a loop that needs to some post statement, I'd use for loop like this:
for (; i < 10; i += 2)
There might be small compiler-dependent differences on the assembly level, but ideally both should behave exactly the same, and the former is more readable. So no, no reson to use the latter version other than nonconformism.
Compile both and check the resulting disassembly, if they are the same (which they probably are). Choose the one you find most readable.
if you want to do something a limited amount of times, then "for" let's you specify the constraint without jumbling it in with the logic inside your loop.
Keeping readability aside for a small while, there is usually no performance difference between the different loops. At least there is no significant difference.
For desktop applications you can chose based on Readability criteria. Refer to the other posts - e.g. looking at for loop someone thinks the incrementor is declared within the loop.
It seems for web applications e.g. client side scripting there might be a difference.
Check this site: http://www.websiteoptimization.com/speed/10/10-2.html
Run your own experiments and go by the results else stick by readability rules.
I can see 2 reasons, none of which I'd consider:
Only have 1 loop construct, but then Kristo's objection stands
write "for (; EVER;)", but then prefer a LOOP_FOREVER macro if really want this.
There really is no difference in C-ish languages between a for (;cond;) loop and a while loop. Generally what I do in C-ish languages is start off writing the loop as a "for" and change it into a "while" if I end up with that form. It is kinda rare though, as you are always iterating through something, and C lets you put any code you want in that last area.
It would be different if C had real (pre-computed iteration) for loops.
You might want to use a do-while loop instead of a for loop so the code is processed at least once before conditions are checked and met (or not).
I used to write some pretty cryptic C/C++ code. Looking back, I would probably do this in a while loop:
ifstream f("file.txt");
char c;
for(f.get(c); !f.eof(); f.get(c)) {
// ...
}
I guess my point is that for loops are usually shorter but less readable, if they're not used in the traditional sense of looping over a range.
This question has been answered - the language has a more natural construct for expressing what you want - you should use it. For example, I can certainly write this:
for (bool b = condition(); b; b = !b) {
/* more code */
}
or:
while (condition()) {
/* more code */
break;
}
instead of the more conventional:
if (condition()) {
/* more code */
}
But why? C (and all languages) have idioms and most of them make rational sense in terms of expressivity and expectation of meaning. When you dick with the idiom, your mess with the sensibilities of the person who has to read your code.

Are multiple conditional operators in this situation a good idea?

I just saw this block of code on the Wikipedia article on conditional operators:
Vehicle new_vehicle = arg == 'B' ? bus :
arg == 'A' ? airplane :
arg == 'T' ? train :
arg == 'C' ? car :
arg == 'H' ? horse :
feet;
I've changed the code a little, but the idea is the same. Would you find this use of the conditional operator acceptable? It's much more concise than the if-else construct, and using a switch would definitely open up a whole new set of opportunities for bugs (fall-throughs anyone?). Also, if-elses and switch can't be used as R-values, so you'd have to create the variable first, initialize it and then assign as necessary.
I for one really like this, but I'm wondering what others think.
But the formatting is essential.
EDIT: I still like this. But I understand those who say "the switch statement was made for this". OK, maybe so. But what if the conditions are function calls that return bool? Or a million other things you can't switch on.
Are you switch lovers really trying to convince me that a huge if-else chain is better? Yes, programmers who don't know how to use the conditional operator will not understand this. They should learn how to use it. It's not arcane.
I have used this type of construction many times. As long as it's formatted nicely (i.e. not all on one line, making it unreadable), I don't see a problem with it.
I would use a switch, because this is the kind of thing it was designed for. Yes, there is a risk of fall-through bugs, but this nested-conditional block has a much higher risk of being misunderstood by other programmers.
This is a great example of conditional operator use. I use it in this manner all the time in C++, Java, and Perl.
Not only is there nothing wrong with it, it communicates the intent of the operation in the most concise and clear way possible.
Replacing with if else, or switch construction requires that the snippet
"new_vehicle = "
be repeated in every instance, which requires that the reader read every repeating instance of it to ensure that it is in fact the same in every instance..
I like it. It is similar to an if-else-if ladder, only more concise.
There's a lot of whitespace around the character constants that makes it a bit hard to read. I'd parenthesize the comparisons: (and maybe move the last value in line.)
Vehicle new_vehicle = (arg == 'B') ? bus :
(arg == 'A') ? airplane :
(arg == 'T') ? train :
(arg == 'C') ? car :
(arg == 'H') ? horse :
feet;
Now it looks great.
The conditional operator version is clean, simple, and it's immediately obvious to anybody who knows C or C++ what is happening. Its other virtue is that it returns a value immediately, which means it can be put in the initialization (like this example is).
A switch statement would be more clumsy. It would require that the variable be declared and then initialized, usually a bad idea if it can be avoided. It would take more typing, and would have more places for bugs to creep in. It wouldn't be as clear, since it would be necessary to look at each case to see that it said something like new_vehicle = foo; break;.
If you're going to do this look up here only, then having the conditional version right there is good, as it immediately shows what's happening. If it's going to occur more than once, consider putting it in a function, so that there's only one place to update if anything changes (such as, say, 'R' for carriage or 'L' for helicopter).
Purely a stylistic choice. For small data sets like you present here, then as long as your programming team isn't thrown by such a thing, then its fine in my book.
A switch is both clearer and possibly much more efficient. If I saw code like this at a code review, I'd be worried. Also, this is "the conditional operator" - it is an instance (albeit currently the only one in C and C++) of a ternary operator.
Vehicle new_vehicle = getVehicleByType(arg);
Vehicle getVehicleByType(char arg){
if (arg == 'B') return bus;
if (arg == 'A') return airplane;
if (arg == 'C') return car;
if (arg == 'T') return train;
if (arg == 'H') return horse;
return feet;
}
I like this better. The nested conditional is clever, but I think this is almost as concise and less likely to confuse a future reader. Sorry if the syntax is off, I'm not doing much C nowadays.
EDIT: Fixed return type omission noted in comments. thx!
EDIT: I'm not horrified at your version by the way. I did not exclaim WTF or OMG when i saw it. I just prefer mine a little more :)
I don't particularly care for it.
It doesn't really buy anything, or make anything more clear, and it's a pretty non-standard usage of the operator.
It seems the primary advantage is that it's somewhat clever. I avoid clever unless there's a pretty good (external) reason to be clever.
I would lean toward a switch statement because the compiler will catch duplicate cases. Arguably, this is not an issue in this example but if the list gets really long and is worked on by several different people it is easy to add a duplicate and not realize it.
Read the C++ section of the Wikipedia article a bit more carefully. It explicitly lists some situations where using the ?: operator is the only option, and can't be replaced by if/else or switch.
On the other hand, I wouldn't use it only because it looks prettier.
I've never seen anything written like this before. While it is clever and well-formatted, this seems like the perfect opportunity to use a dictionary/hashtable (assuming Vehicle is an enumeration, which is unclear).
A few people have already mentioned the possibility of using an std::map or other associative array type to do the job. As long as you're only doing this is one place (or a few places), you might consider just using a normal array or vector instead:
Vehicle vehicles[CHAR_MAX];
// Initialization
std::fill_n(vehicles, CHAR_MAX, feet);
vehicles['A'] = airplane;
vehicles['B'] = bus;
vehicles['C'] = car;
vehicles['H'] = horse;
vehicles['T'] = train;
// Use
Vehicle new_vehicle = vehicles[arg];
Depending on how may tables you need/use (that store the same type of object), and the size of the contained objects (Vehicle in this case), this can be a perfectly reasonable alternative to an std::map. If you're creating a lot of tables, or each object is really big, std::map becomes a more reasonable alternative.
When you use an std::map (or unordered_map, etc.) you're using more code to save on data storage. This does the reverse -- but as long as Vehicle is small (say, 4 bytes), one table like the above will typically occupy something like half a kilobyte. It's hard to guess exactly how large the code for std::map is going to be for a specific compiler, but it seems likely that it'll usually be larger than half a kilobyte, so if you're only creating one table like this, std::map may be a net loss.
Of course, if you know that you're only dealing with letters as input, you could reduce the table size quite a bit:
template <class T>
class letter_table {
static const int range = 'Z' - 'A';
T table[range];
public:
// ...
T operator[](int index) {
index -= 'A';
assert(index<range);
return table[index];
}
};
In the example case, this would give a table around 100 bytes -- you can create a pretty fair number of 100-byte tables in the space std::map will normally occupy.
Purely practical:
Plus: The ternary sequence is more flexible, and can be used to avoid the limitations of switch, you could use other operators (e.g. <=, >=) or any other tests, including e.g. string comparisons.
x = IsEven(arg) ? 0 :
(arg < 0) ? -1 : 1; // or whatever
Also, if the switch is a performance bottleneck and you have uneven probabilities, you can force the likeliest tests being done first (due to the not evaluated guarantee for the path not chosen).
So-So
Unlike a switch statement, order is important (unless you stick to ==). That can be an advantage, but being otherwise similar to switch that might be misleading when the maintainer is unfamiliar with the concept or in a hurry.
Many developers may shy away because they aren't sure about the details (which terms will be evaluated, is the rpecedence of the operators ok?) - However, if your developer pool won't grasp a well-presented example, you might have problems that can't be solved by banning ternary operators.
Minus
It isn't as common as switch, thus the optimizer might not treat it the same. Optimizers are know to select the best fit implementation for a switch (table, binary search, comparison sequence, or any combination of this). Optimizers can't reaarange wevaluaiton order, and are less likely to support a table lookup here.
Requires good formatting to be easily recognizable (lining up the '?' and ':') - Sucks when you use tabs.
Aesthetics
I like it for its precision and succinctness, close to mathematical notations. However, that might as well be used against it. It's probably an eyebrow raiser at code reviews, because it is less common and more brittle.
Just for comparison, in C++0x you can have an expression without using the conditional operator or an out-of-line function:
Vehicle new_vehicle = [&]() -> Vehicle {
if (arg == 'B') return bus;
if (arg == 'A') return airplane;
if (arg == 'T') return train;
if (arg == 'C') return car;
if (arg == 'H') return horse;
return feet;
}();
Not really any better, though.
How about:
enum Vehicle { bus = 'B', airplane = 'A', train, car = 'C', horse = 'H', feet = 'F' };
...
new_vehicle = arg;
:-), by the way.
I think its useful for someone who code it, but will be difficult to understand for the reviwer,
"KEEP IT SIMPLE BUDDY"
In my opinion what you've done is acceptable due to the simplicity of the example. If you were doing more things with each case this type of construct could rapidly get messy. For that reason I would prefer a switch or even nested if then elses (if there aren't too many cases), formatted as follows:
if (A) {
//Do A stuff
}
else if (B) {
//Do B stuff
}
else if (C) {
//Do C stuff
}
else {
//Do default stuff
}
It's about the readability of the code, which lends itself to the maintainability of the code. I've never been a huge fan fan of the conditional operator, because I don't like to see multiple expressions on a single line. Conditional operators can be difficult to follow when single stepping the code in a debugger. The simpler the code the easier it is to concentrate on what the code is doing.
Many of us are used to the Iif-functions in various reporting tools or the If-function in Excel where we basically need to use the less clear Iif(arg="B";"Bus";Iif(arg="A";Airplane;"Feet")). I love your sample compared to that :)
I personally would've used if-elses, but I would not have a problem with your sample.