Fun with C++: When Parentheses Don’t Call!

Just a quick one. Most of us have come to think of parentheses as sometimes necessary but always harmless little creatures. Our view is that unless parentheses change the order of evaluation (and therefore the meaning) of an expression, they are completely harmless.
Well, while that is true almost by definition, there are cases that we might not realize the meaning of an expression is being changed. One such case is in the presence of the versatile and extremely useful (and possibly massively pain inducing) comma operator (’,‘.)
You see, the comma has two uses in C++ language: as an operator and as a separator for many different lists of things in the language grammar (function arguments, object declaration, etc.) In the first context, a pair of parentheses work exactly as anyone would expect; in the second, probably not. The problem is that the use of commas in the first context is not common at all, while the commas of second kind riddle any and every C++ source file. It’s mixing the meaning of the uses that’ll lead to potentially cryptic compile errors (what a shocker! Cryptic compile error messages in C++?!), or worse, logical bugs that will make you wish you were coding in machine language.

Just for the sake of comprehensiveness, while I know that you all know how the comma operator operates, I’m gonna describe its workings anyway. The comma operator has the lowest precedence among all the operators in C++ and it is a left-associative operator (meaning “a, b, c” will be evaluated as “(a, b), c“.)
An expression of the form “a, b” (where a and b are expressions themselves (and not statements)) will always evaluate a first, and then b and will return the evaluated value of b.
Please believe me when I say that overloading this operator can achieve magic and convenience one can only dream of in C++! Just see the Boost.Assignment library for a sample.

One place that the implicit mixing up of the meaning of comma could cause trouble is in function calls. Suppose that a function X accepts two arguments. Now, if instead of X (a, b) someone writes X ((a, b)) then they will be given a compile error saying that function X does not take one argument(s);-) . This is because the extra pair of parens have changed the meaning of the comma from a simple argument list separator to a C++ operator.
This will not confuse any C++ programmer out of her first few months with the language, and will be obvious and easy to find and fix. But what if the extra pair of parentheses is the result of a macro expansion? Then you’ll have to chase down the macro definition. Still not very hard, because the compiler will catch you. But what happens if the X function has an overload with a single argument, incidentally with a type compatible with b? What happens if the function takes more than two arguments and has many overloads and it’s called like this: X (a, (b, c), d, (e, f, g))?
I should probably list this as another macro pitfall. Take care where you put parentheses! Don’t just slap them whenever you’re in doubt!

Printer-friendly version Printer-friendly version