条件语句中逗号的优点是什么?

What is the advantage of commas in a conditional statement?

我们可以将if语句写成

if (a == 5, b == 6, ... , thisMustBeTrue)

只有最后一个条件可以进入if主体。

为什么允许?

没有优点:逗号运算符只是一个表达式,其类型为表达式列表中最后一个表达式的类型,而 if 语句计算布尔表达式。

if(<expr>) { ... }
 with type of <expr> boolean

这是一个奇怪的运算符,但它并没有什么神奇之处 - 除了它会混淆函数调用中的表达式列表和参数列表。

foo(<args>)
 with <args> := [<expr>[, <expr>]*]

请注意,在参数列表中,逗号对分隔参数的约束力更强。

简而言之: 尽管这样做是合法的,但在 ifwhile 语句的条件部分使用逗号运算符通常没有意义(编辑:尽管后者有时可能会有所帮助,如 user5534870在他的回答中解释。

更详尽的解释: 除了它的句法功能(例如在初始化列表、变量声明或函数 calls/declarations 中分隔元素),在 C 和 C++ 中,, 也可以是一个普通的 运算符 就像例如+,所以它可以在任何地方使用,只要表达式是允许的(在 C++ 中你甚至可以重载它)。
与大多数其他运算符的不同之处在于 - 尽管双方都得到评估 - 它不会以任何方式组合左右表达式的输出,而只是 returns 正确的。
之所以引入它,是因为某人(可能是 Dennis Ritchie)出于某种原因决定 C 需要一种语法来在一个位置编写两个(或更多)不相关的表达式,而您通常只能在其中编写一个表达式。

现在,if 语句的条件是(除其他外)这样的地方,因此,您也可以在那里使用 , 运算符 - 无论这样做是否有意义是一个完全不同的问题!特别是 - 并且不同于例如函数调用或变量声明 - 逗号在那里没有特殊含义,所以它确实如此,它总是这样做:它计算左右表达式,但只有 returns 右边的结果,然后是由 if 语句使用。

我现在能想到的只有两点,使用 (non-overloaded) ,-operator 是有意义的:

  1. 如果要在 for 循环的头部递增多个迭代器:

    for ( ... ; ... ; ++i1, ++i2){
        *i2=*i1;
    }
    
  2. 如果要在 C++11 constexpr 函数中计算多个表达式。

再重复一次:在 ifwhile 语句中使用逗号运算符 - 按照您在示例中展示的方式 - 不是明智之举。这只是另一个示例,其中 C 和 C++ 的语言语法允许您编写代码,但其行为方式与乍看之下所期望的不同。还有很多....

None 随便吧。该代码中 a 的比较完全是多余的。

对于 if 语句,将某些内容放入逗号表达式而不是外部没有任何实际意义。

对于while语句,在条件中加入逗号表达式,进入循环时执行第一部分eitheror 循环时。如果不重复代码,就无法轻易复制。

那么 s do...while 语句怎么样?在那里我们只需要担心循环本身,对吗?事实证明,即使在这里,也不能通过将第一部分移动到循环中来安全地替换逗号表达式。

一方面,循环体中变量的析构函数不会已经 运行 那么这可能会有所不同。另一方面,循环内的任何 continue 语句将到达逗号表达式的第一部分 只有 当它确实在条件中而不是在循环体中时。

稍微改变一下你的例子,假设是这样的

if ( a = f(5), b = f(6), ... , thisMustBeTrue(a, b) )

(注意 = 而不是 ==)。在这种情况下,逗号保证了从左到右的评估顺序。相比之下,有了这个

if ( thisMustBeTrue(f(5), f(6)) )

你不知道 f(5) 是在 f(6) 之前还是之后调用的。

更正式地说,逗号允许您编写 表达式序列 (a,b,c),就像您可以使用 ; 编写语句序列一样a; b; c;。 就像 ; 创建 sequence point (end of full expression) so too does a comma. Only sequence points govern the order of evaluation, see this post.

但当然,在这种情况下,您实际上会这样写

a = f(5);
b = f(6);    
if ( thisMustBeTrue(a, b) )

那么什么时候逗号分隔的表达式序列比 ; 分隔的语句序列更可取?我几乎从不说。也许在宏中,当您希望右侧替换为单个表达式时。

接下来的内容有点牵强,具体取决于您希望变得多么狡猾。

考虑这样一种情况,函数 return 通过修改通过引用或通过指针传递的参数(可能来自设计不当的库,或者确保该值不会因未分配而被忽略)而成为一个值在 returning 之后,随便什么)。

void calculateValue(FooType &result) {/*...*/}

那依赖result的条件语句怎么用呢?

您可以声明要修改的变量,然后用 if 检查它:

FooType result;
calculateValue(result);
if (result.isBared()) {
    //...
}

这可以缩短为

FooType result;
if (calculateValue(result) , result.isBared()) {
    //...
}

这真的不值得。 但是,对于while 循环可能会有一些小的优势。如果 calculateValue should/can 被调用直到结果不再是 bar'd,我们会有类似的东西:

FooType result;
calculateValue(result);  //[1] Duplicated code, see [2]
while (result.isBared()) {
    //... possibly many lines
    //separating the two places where result is modified and tested

    //How do you prevent someone coming after you and adds a `continue`
    //here which prevents result to be updated in the and of the loop?

    calculateValue(result); //[2] Duplicated code, see [1]
}

可以压缩为:

FooType result;
while (calculateValue(result) , result.isBared()) {
    //all your (possibly numerous) code lines go here
}

这样更新 result 的代码只在一个地方,并且靠近检查其条件的那一行。

可能不相关:变量可以通过参数传递更新的另一个原因是,除了 modify/return 计算值之外,函数还需要 return 错误代码。在这种情况下:

ErrorType fallibleCalculation(FooType &result) {/*...*/}

然后

FooType result;
ErrorType error;

while (error = fallibleCalculation(result) , (Success==error && result.isBared())) {
    //...
}

但如评论中所述,您也可以在不使用逗号的情况下执行此操作:

FooType result;
ErrorType error;

while (Success == fallibleCalculation(result) && result.isBared()) {
    //...
}

我的问题是 ifwhile 语句中逗号的优点是什么?为什么允许?

它存在是因为语句和表达式在 C 中是不同的东西。复合 表达式 是一种从理论(和其他一些语言)中理解的结构,如果没有以逗号的形式添加。它在 for 声明中的使用是他们需要它的最初理由。

但是,通过从合理的理论角度使语言更加完整,它后来发现了没有人计划的用途。早期的 C++ 是生成 C 作为其输出的翻译器,并且具有顺序 表达式 对于允许内联函数在 C 代码中真正生成 "in line" 逻辑是绝对必要的。

包括表达式出现的任何地方,包括 if 语句的条件。

同样,在"interesting"宏中也使用过。尽管 C++ 通过提供内联函数取消了宏,但直到 x11 的编译器发现 Boost FOREACH 范围循环(最终,对添加到 x11 语言中的功能的模拟)非常方便,这就是一组非常聪明的宏,涉及逗号运算符。

(嗯,当前版本使用链式 if/else 扩展为多个语句,而不是将其全部塞入单个 while。)

现在,有另一种方法可以将任何语句放入表达式 (lambda),因此未来可能不需要 需要 不再使用它了。


所以,不要那样写代码。除非它很清楚并且确实比编写辅助函数或拆分成多个语句更简单。

但这可能只是您想要在一个地方轻松使用的宏,而那个地方在 ifwhile 的括号内。这可以用托管在 C++ 源代码中的 领域特定语言 语言仿真功能 来证明(也许)是异常的替代方法嵌入式实时系统中使用的处理。

总之,没有正常的好用。但它的存在是为了完整性,您永远不知道什么时候有人会发现它有用。