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

What is the advantage of commas in a conditional statement?

本文关键字:是什么 语句 条件      更新时间:2023-10-16

我们可以将if语句写成

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

并且只有最后一个条件应该满足才能进入CCD_ 2主体。

为什么允许?

稍微更改一下您的示例,假设是这个

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

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

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

您不知道CCD_ 5是在CCD_。

更正式地说,逗号允许您编写表达式(a,b,c)序列,就像使用;编写语句序列a; b; c;一样。正如;创建一个序列点(完整表达式的末尾)一样,逗号也是如此。只有顺序点决定评估的顺序,请参阅本文。

当然,在这种情况下,你实际上应该写这个

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

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

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

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

现在,if语句的条件(以及其他条件)就是这样一个地方,因此,您也可以在那里使用,运算符——这样做是否有意义是一个完全不同的问题!特别是,与例如函数调用或变量声明不同,逗号在那里没有特殊意义,所以它总是这样做:它向左和向右计算表达式,但只返回右表达式的结果,然后由if语句使用。

我现在唯一能想到的两点是,使用(非重载,-运算符有意义的地方是:

  1. 如果您想在if0循环的头部增加多个迭代器:

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

再重复一遍:在ifwhile语句中使用逗号运算符——就像你在例子中展示的那样——是不明智的。这只是C和C++语言语法允许你编写代码的另一个例子,乍一看,它的行为并不像人们所期望的那样。还有很多。。。。

对于if语句,将某些内容放入逗号表达式而不是外部并没有真正的意义。

对于while语句,将逗号表达式置于条件中会在进入循环时执行第一部分,在循环时执行。如果没有代码复制,就无法轻松复制。

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

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

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

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

这是一个奇怪的运算符true,但它并没有什么魔力——只是它混淆了函数调用中的表达式列表和参数列表。

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

请注意,在参数列表中,逗号与分隔参数的绑定更强。

接下来的内容有点牵强,这取决于你可能想要的狡猾程度

考虑这样一种情况,即函数通过修改通过引用或指针传递的参数来返回值(可能来自设计糟糕的库,或者为了确保该值不会因返回后未赋值而被忽略,不管怎样)。

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

那么,如何使用依赖于result的条件语句呢?

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

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

这可以缩短为

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

这真的不值得然而对于while循环,可能存在一些小的优势。如果calculateValue应该/可以被调用,直到结果不再是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的代码只在一个地方,并且在检查其条件的行附近。

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

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()) {
    //...
}

无任何内容。该代码中对a的比较是完全多余的。

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

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

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

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

同样,它也被用于"有趣"的宏中。正如C++通过提供内联函数来废除宏一样,直到up-to-x11编译器发现Boost FOREACH范围循环(最终,对x11中添加到语言中的功能进行了模拟)非常方便,这是一组非常聪明的宏,涉及逗号运算符。

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

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


所以,不要这样写代码。除非它比编写辅助函数或拆分为多个语句更清楚、更简单。

但是,对于一个宏来说,它可能只是你想在一个地方轻松使用的东西,而这个地方在ifwhile的parens内部。这可以在C++源代码中托管的域特定语言中得到证明,也可以在语言模拟功能(也许)中作为嵌入式实时系统中使用的异常处理的替代方案。

简而言之,它没有一个正常的良好使用。但它是为了完整性而存在的,你永远不知道什么时候会有人觉得它有用。