使用逗号运算符是一种好习惯吗?

时间:2011-11-21 23:08:02

标签: c++ c operators

我最近(实际上仅在SO上)遇到了C / C ++逗号运算符的使用。据我所知,它在左侧和右侧操作符之间的线上创建了一个序列点,以便您具有可预测(定义)的评估顺序。

我对为什么会在语言中提供这一点感到有点困惑,因为它似乎是一个可以应用于首先不起作用的代码的补丁。我发现很难想象一个可以使用的地方并不过分复杂(并且需要重构)。

有人可以解释一下这种语言功能的用途吗?它可以在实际代码中使用(在合理范围内),如果有的话?

10 个答案:

答案 0 :(得分:17)

它在while()循环的条件下很有用:

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

这样可以避免必须复制update_thing()行,同时仍然保持退出条件在while()控制表达式中,您希望在其中找到它。它也适用于continue;

它在编写计算值的复杂宏时也很有用。

答案 1 :(得分:9)

逗号运算符只是分隔表达式,因此您可以执行多项操作,而不只是只需要一个表达式的操作。它可以让你做像

这样的事情
             (x)              (y)
for (int i = 0, j = 0; ...; ++i, ++j)

请注意,x不是逗号运算符,y是。

你真的不必考虑它。它有一些更神秘的用途,但我不相信它们是绝对必要的,所以它们只是好奇心。

答案 2 :(得分:5)

for循环结构中,它是有意义的。虽然我在这种情况下通常会发现它们更难阅读。

在SO上激怒你的同事和人也非常方便。

bool guess() {
  return true, false;
}

答案 3 :(得分:4)

扮演魔鬼的辩护律师,反驳这个问题可能是合理的:

  

总是使用分号终止符是不错的做法?

有些观点:

  • 用逗号替换大多数分号将立即使大多数C和C ++代码的结构更加清晰,并消除一些常见错误。
  • 这更像是功能性编程的风格而非命令式。
  • Javascript的'自动分号插入'是其有争议的句法特征之一。

这种做法是否会增加“常见错误”尚不清楚,因为没有人这样做。

但是当然如果你这样做了,你可能会惹恼你的同事们,并成为SO的贱民。

修改:请参阅AndreyT's 2009年Uses of C comma operator的优秀答案。 Joel 2008还讨论了C#/ C / C ++中的两个并行语法类别。

作为一个简单的例子,while (foo) a, b, c;的结构很清楚,但while (foo) a; b; c;在缺少缩进或大括号或两者的情况下都会产生误导。

编辑#2 :正如AndreyT所述:

  

[C] C语言(以及C ++)在历史上是两种完全不同的编程风格的混合,可以称之为“语句编程”和“表达式编程”。

但他断言“在实践中 声明 编程会产生 很多 更易读的代码”[强调添加]显然 false 。在您看来,使用他的示例,以下哪两行更具可读性?

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; 

回答 不可读。它是 white space ,它提供了可读性 - 对于Python来说是一种欢呼!第一个是更短的。但是如果你有一个Hazeltine终端,那么分号版本确实有更多像素的黑色空间绿色空间 - 这可能是真正的问题吗?

答案 4 :(得分:2)

每个人都说它经常用于for循环,这是真的。但是,我发现它在for循环的条件语句中更有用。例如:

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

在没有逗号运算符的情况下重写它将需要至少执行一些我不太满意的事情,例如在使用它的范围外声明x,或者在{{1}的第一次调用时使用特殊外壳}。

我也正在策划如何利用C ++ 11 constexpr函数来使用它,因为我猜它们只能由单个语句组成。

答案 5 :(得分:1)

我认为唯一常见的例子是for循环:

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

c-faq中所述:

  

有一段时间,你会发现自己处于C期望的状态   单表达,但你想说两件事。最多   common(实际上唯一常见的)示例是for循环,   特别是第一和第三控制表达。

答案 6 :(得分:1)

我能想到的唯一合理用途是for构造

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

因为它允许同时增加多个变量,仍然保持for构造结构(易于阅读和理解为受过训练的眼睛)。

在其他情况下,我同意这是一个糟糕的黑客......

答案 7 :(得分:1)

逗号运算符对于将序列放在无法插入代码块的位置非常有用。正如所指出的,这在编写紧凑且可读的循环时非常方便。此外,它在宏定义中很有用。以下宏增加了警告的数量,如果设置了布尔变量,也会显示警告。

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

这样你就可以写(例1):

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

逗号运算符实际上是一个糟糕的mans lambda函数。

答案 8 :(得分:1)

我还使用逗号运算符将相关操作粘合在一起:

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

答案 9 :(得分:0)

虽然在批准C ++ 11几个月后发布,但我在这里看不到任何与constexpr函数相关的答案。 This answer与一个不完全相关的问题引用了对逗号运算符的讨论及其在常量表达式中的用处,其中专门提到了新的constexpr关键字。

虽然C ++ 14确实放宽了对constexpr函数的一些限制,但仍然有用的是注意逗号运算符可以在constexpr函数内授予可预测的有序操作,例如(来自上述讨论):

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

甚至可能是:

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

这是否有用完全取决于实现,但这些只是可以在constexpr函数中应用逗号运算符的两个简单示例。