使用逗号操作符是一个好习惯吗?

13
我最近(只在SO上)遇到了使用C/C++逗号运算符的情况。据我所知,它在左右操作数之间的行上创建一个序列点,以便您有一个可预测(定义)的评估顺序。
我有点困惑为什么会在语言中提供它,因为它似乎是可以应用于本不应该工作的代码的补丁。我很难想象它可以被用于一个不过度复杂(需要重构)的地方。
如果可以,请有人解释一下此语言功能的目的以及它在真实代码中可能的用途(合理范围内)。

这里有一个巧妙的用法:https://dev59.com/I1zUa4cB1Zd3GeqP4505#7906060 - R. Martinho Fernandes
6
@R.MartinhoFernandes: 聪明?我认为你误解了大局。在“高级”C++元编程中没有什么聪明的地方,只有虐待狂。 - 6502
10个回答

21

这在 while() 循环的条件中可能很有用:

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

这样可以避免重复使用update_thing()代码行,同时仍然在while()控制表达式中保持退出条件,这也是你期望找到的地方。它还与continue;很好地配合使用。

这也适用于编写评估为值的复杂宏。


谢谢大家 :) 很有趣,其实我以前在循环中使用过它,但直到我问了这个问题,我才意识到它是一个操作符。 (我知道,很明显所以我感到有点傻!:p) - John Humphreys
同样地,我见过 while (update_thing(y), test_thing(y))。它允许你将更新和测试分开成两个单独的函数以提高可读性,并且在循环中仍然可以理解 -- 当(等等,让我们更新这个东西,好的现在--)这个东西测试为真时,执行这些操作。 - anon

9
逗号运算符只是用于分隔表达式,因此您可以在只需要一个表达式的地方做多个操作。它使您能够执行以下操作:
             (x)              (y)
for (int i = 0, j = 0; ...; ++i, ++j)

请注意,x不是逗号运算符,但y是。
你真的不需要考虑它。它有一些更加深奥的用法,但我不认为它们绝对是必需的,所以它们只是好玩的东西。

7

for 循环结构内使用它是有意义的。但我通常发现在这种情况下更难阅读。

这也是激怒你的同事和 Stack Overflow 上的人的好方法。

bool guess() {
  return true, false;
}

4
在变量声明中的逗号不是逗号运算符的使用,逗号运算符产生值。 - lhf

5
Playing Devil's Advocate,反过来问也许是合理的:
始终使用分号终止符是否是好的实践?
一些要点:
用逗号替换大多数分号将立即使大多数C和C++代码的结构更清晰,并消除一些常见错误。
这更像是函数式编程而不是命令式编程。
Javascript的“自动分号插入”是其中一个有争议的语法特性。
由于没有人这样做,因此无法确定这种做法是否会增加“常见错误”。
但是,如果您确实这样做,您可能会惹恼您的程序员同事,并成为SO上的社交避忌者。
编辑:请参见AndreyT 2009C逗号运算符的用途的精彩回答。 Joel 2008也谈到了C#/ C / C ++中的两个平行语法类别。
作为一个简单的例子,while (foo) a, b, c; 的结构是清晰的,但是在缺少缩进或大括号的情况下,while (foo) a; b; c; 是具有误导性的,或者两者都缺失。 编辑 #2:正如AndreyT所述:

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

但他声称“实际上语句编程产生的可读代码要多得多” [已加重音] 明显是错误的。使用他的例子,你认为以下哪一行更易读?
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; 
回答: 它们难以阅读。是空格使得内容易读 - Python太棒了!第一个更短。但分号版本有更多的黑色空间或者绿色空间(如果你使用Hazeltine终端)- 这可能是真正的问题?

考虑相反的立场总是很有趣的,但话说回来,我想看到更多证据支持你的观点,因为在大多数情况下,逗号和分号之间的语义差异非常小。除了对于(;;) a, b, c而言,与for (;;) a; b; c;截然不同。 - Bowie Owens
@Bowie:我承认我的回答中没有任何证据,但你提供的两个例子很好:第一个for (;;)后面紧跟着a、b、c;,很清晰易懂;但第二个例子for (;;) a; b; c;在缺少缩进或括号的情况下可能会让人产生错误理解。 - Joseph Quinsey
1
既然“占领华尔街”运动似乎正在消退,也许我们可以把注意力重新转向逗号这个重要问题了? - Joseph Quinsey

2

大家都说常常在for循环中使用它,这是真的。然而,我认为在for循环的条件语句中更加有用。例如:

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

如果不使用逗号运算符来重写这段代码,至少需要做以下几件事情,而这些事情都让我有些不太舒服,比如将变量x声明在它被使用的作用域之外,或者特殊处理对get_x()的第一次调用。

同时,我在考虑如何在C++11的constexpr函数中利用逗号运算符,因为我猜想这些函数只能由单个语句组成。


1
你可以写成(x=get_x())!=sentinel,但我认为你的方式更清晰。 - lhf

1

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

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

c-faq中所述:

有时候,你会发现自己处于这样一种情况:C语言需要一个表达式,但你却有两个想要表达的东西。最常见(实际上也是唯一常见)的例子就是在for循环中,特别是第一个和第三个控制表达式。


2
在第一部分(你的例子中),它不是逗号运算符。 - 6502
3
@MByD说的是一个逗号,但它并没有使用逗号运算符。在声明多个变量时,逗号是允许的。右边的部分确实是逗号运算符。 - Mooing Duck
我的意见只是因为引文是关于第一部分和第三部分的。可能,鉴于引文,最好将ij的声明放在for循环外面(就像在C中一样),只留下双重初始化。 - 6502

1
我能想到的唯一合理用途是在 for 结构中。
for (int count=0, bit=1; count<10; count=count+1, bit=bit<<1)
{
    ...
}

因为它允许同时增加多个变量,仍然保持for结构的形式(对于训练有素的人来说易于阅读和理解)。

在其他情况下,我同意这是一种不好的hack...


count = count + 1bit = bit << 1 - Seth Carnegie
@SethCarnegie:是的。原始版本是 count++,bit<<=1,但我认为这种方式更易读。 - 6502

1
逗号运算符在无法插入代码块的地方放置序列非常有用。正如指出的那样,在编写紧凑且易读的循环时非常方便。此外,它在宏定义中也很有用。以下宏会增加警告数量,并且如果设置了布尔变量,还将显示警告。
#define WARN if (++nwarnings, show_warnings) std::cerr

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

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

逗号运算符实际上是一个穷人的 Lambda 函数。


是的,可能是@kaizer.se,但我已经开始重视实用主义了,这个宏可以完成工作。 - Bowie Owens

1

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

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

如果在你的例子中,,被替换为;,会有什么不同吗?虽然看起来略有不同,但在这种情况下,“粘合在一起”的意思是什么? - TobiMcNamobi
这里不会有任何区别。但是这里会有区别:while (1) ++i, ++j。粘合在一起意味着逻辑上将这些语句分组,以便清楚地表明这些操作紧密相关。 - perreal

0

尽管是在C++11被批准几个月后发布的,但我没有看到与constexpr函数相关的任何答案。这个答案对一个不完全相关的问题进行了引用,提到了逗号运算符及其在常量表达式中的有用性,新的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函数中应用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接