简体   繁体   中英

Is it good practice to use the comma operator?

I've recently (only on SO actually) run into uses of the C/C++ comma operator. From what I can tell, it creates a sequence point on the line between the left and right hand side operators so that you have a predictable (defined) order of evaluation.

I'm a little confused about why this would be provided in the language as it seems like a patch that can be applied to code that shouldn't work in the first place. I find it hard to imagine a place it could be used that wasn't overly complex (and in need of refactoring).

Can someone explain the purpose of this language feature and where it may be used in real code (within reason), if ever?

It can be useful in the condition of while() loops:

while (update_thing(&foo), foo != 0) {
    /* ... */
}

This avoids having to duplicate the update_thing() line while still maintaining the exit condition within the while() controlling expression, where you expect to find it. It also plays nicely with continue; .

It's also useful in writing complex macros that evaluate to a value.

The comma operator just separates expressions, so you can do multiple things instead of just one where only a single expression is required. It lets you do things like

             (x)              (y)
for (int i = 0, j = 0; ...; ++i, ++j)

Note that x is not the comma operator but y is.

You really don't have to think about it. It has some more arcane uses, but I don't believe they're ever absolutely necessary, so they're just curiosities.

Within for loop constructs it can make sense. Though I generally find them harder to read in this instance.

It's also really handy for angering your coworkers and people on SO.

bool guess() {
  return true, false;
}

Playing Devil's Advocate, it might be reasonable to reverse the question:

Is it good practice to always use the semi-colon terminator?

Some points:

  • Replacing most semi-colons with commas would immediately make the structure of most C and C++ code clearer, and would eliminate some common errors.
  • This is more in the flavor of functional programming as opposed to imperative.
  • Javascript's 'automatic semicolon insertion' is one of its controversial syntactic features.

Whether this practice would increase 'common errors' is unknown, because nobody does this.

But of course if you did do this, you would likely annoy your fellow programmers, and become a pariah on SO.

Edit: See AndreyT's excellent 2009 answer to Uses of C comma operator . And Joel 2008 also talks a bit about the two parallel syntactic categories in C#/C/C++.

As a simple example, the structure of while (foo) a, b, c; is clear, but while (foo) a; b; c; while (foo) a; b; c; is misleading in the absence of indentation or braces, or both.

Edit #2 : As AndreyT states:

[The] C language (as well as C++) is historically a mix of two completely different programming styles, which one can refer to as "statement programming" and "expression programming".

But his assertion that "in practice statement programming produces much more readable code" [emphasis added] is patently false . Using his example, in your opinion, which of the following two lines is more readable?

a = rand(), ++a, b = rand(), c = a + b / 2, d = a < c - 5 ? a : b;
a = rand(); ++a; b = rand(); c = a + b / 2; if (a < c - 5) d = a; else d = b; 

Answer : They are both unreadable. It is the white space which gives the readability--hurray for Python!. The first is shorter. But the semi-colon version does have more pixels of black space , or green space if you have a Hazeltine terminal--which may be the real issue here?

Everyone is saying that it is often used in a for loop, and that's true. However, I find it's more useful in the condition statement of the for loop. For example:

for (int x; x=get_x(), x!=sentinel; )
{
    // use x
}

Rewriting this without the comma operator would require doing at least one of a few things that I'm not entirely comfortable with, such as declaring x outside the scope where it's used, or special casing the first call to get_x() .

I'm also plotting ways I can utilize it with C++11 constexpr functions, since I guess they can only consist of single statements.

I think the only common example is the for loop:

for (int i = 0, j = 3; i < 10 ; ++i, ++j)

As mentioned in the c-faq :

Once in a while, you find yourself in a situation in which C expects a single expression, but you have two things you want to say. The most common (and in fact the only common) example is in a for loop, specifically the first and third controlling expressions.

The only reasonable use I can think of is in the for construct

for (int count=0, bit=1; count<10; count=count+1, bit=bit<<1)
{
    ...
}

as it allows increment of multiple variables at the same time, still keeping the for construct structure (easy to read and understand for a trained eye).

In other cases I agree it's sort of a bad hack...

The comma operator is useful for putting sequence in places where you can't insert a block of code. As pointed out this is handy in writing compact and readable loops. Additionally, it is useful in macro definitions. The following macro increments the number of warnings and if a boolean variable is set will also show the warning.

#define WARN if (++nwarnings, show_warnings) std::cerr

So that you may write (example 1):

if (warning_condition)
    WARN << "some warning message.\n";

The comma operator is effectively a poor mans lambda function.

I also use the comma operator to glue together related operations:

void superclass::insert(item i) {
  add(i), numInQ++, numLeft--;
}

Though posted a few months after C++11 was ratified, I don't see any answers here pertaining to constexpr functions. This answer to a not-entirely-related question references a discussion on the comma operator and its usefulness in constant expressions, where the new constexpr keyword was mentioned specifically.

While C++14 did relax some of the restrictions on constexpr functions, it's still useful to note that the comma operator can grant you predictably ordered operations within a constexpr function, such as (from the aforementioned discussion):

template<typename T>
constexpr T my_array<T>::at(size_type n)
{
    return (n < size() || throw "n too large"), (*this)[n];
}

Or even something like:

constexpr MyConstexprObject& operator+=(int value)
{
    return (m_value += value), *this;
}

Whether this is useful is entirely up to the implementation, but these are just two quick examples of how the comma operator might be applied in a constexpr function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM