• 关于C++条件运算符(三目运算符)右结合的说明


    C++条件运算符 a ? b : c ; 是右结合的,但是这个右结合要怎么理解呢?

    对于a ? b : c ? d : e这样的表达式如果按照右结合来解读的话,那不应该是先运算c,然后返回d或者e,返回后再参与到a ? b : d / e;这样的运算的吗?

    但实际代码的编译测试的结果显然大家都已经知道并非如此,是先计算a,或者返回b,或者返回 (c ? d : e)这个整体的结果。

    为什么是先计算a,而不是先计算c?右结合到底要怎么理解?网上的回答基本上都是错误的,个别的说法是对的,但是不完全准确,下面就具体说说这个右结合的理解。

    先来看三个例子:

    1. a + b*c - d;
    
    2. a = b = c;
    
    3. f()*g() / g();

    上面的这三个例子分别涉及了表达式运算符的优先级、结合律、求值顺序。

    对于第一个例子,因为乘法运算符(*)的优先级比加减运算符("+","-")的优先级高,所以乘法优先,乘法结合的更紧密,故而第一个例子相当于 a + (b*c) - d;

    对于第二个例子,同一个运算对象b,有两个赋值运算符("="),同一运算符或者属于同一组的运算符的优先级是相等的,这时就无法根据优先级来判断谁优先,谁结合紧密,因此需要另外的东西来判定,这就是结合律。

    结合律只用于表达式中同一个对象左右出现两个相同优先级的操作符的情况,用于消除歧义。相当于添加()来调节优先级

    根据结合律来判断谁优先结合或者结合的更紧密,由于赋值运算符("=")是右结合的,因此右边优先,将b及其右边视为一个整体,从右向左计算,即 a = b = c;相当于 a = (b = c);而b=c;又是一个子表达式,先对子表达式进行运算,即将c的值赋给b,赋值运算完毕后返回赋值运算符左侧的对象,即返回b,然后继续参与a=b;的运算。

    同一组运算符的意思是指几个运算符优先级相同,属于一个分组,C++中将不同优先级分成了若干组,比如"+"和"-"就属于同一组。

     从第一个和第二个例子可以看到,结合律确定表达式计算方向。第一个例子左结合,从左向右计算,第二个例子右结合,从右向左计算。

    对于第三个例子,是两个优先级相同,但是是不同的运算符,根据结合律从左向右结合,进行计算。这里就会出现2个名词,一个是表达式的计算顺序,或者叫表达式的计算方向,另一个叫运算对象的求值顺序。在第三个例子当中,由于是左结合的,因此表达式的计算方向是从左往右。但是毕竟f( )和g( )是两个函数参与运算的是函数的返回值,而不是函数,因此需要事先对这两个函数进行调用,调用完毕后,将返回值拿过来参与运算,假设f( )和g( )的返回值分别是f和g,即最终参与表达式运算的是f * g / g,这里确定了是返回值f先与返回值g相乘,然后再与返回值g做除法运算。但是有没有规定先调用f( )呢?没有!!!没有!!!没有!!!重要的事情说三遍,C++只确定了表达式的计算方向,并没有规定要先获取哪个参与运算的对象

    到这里,我们已经知道了表达式的两个行为特征了,如下:

    1. 复合表达式是会考虑优先级和结合律的。

    2. 运算对象的求值顺序与优先级、结合律没有关系。

    大多数运算符都没有规定表达式中运算对象的求值顺序,对于互不影响的函数之间,这并没有什么问题,但如果这几个函数共同影响同一个全局变量就会出现问题。

    因此在C++ Primer第五版的123页中才会有这么一说:

    “因为表达式的行为不可预知,因此不论编辑器生成什么样的代码程序都是错误的。”

    是的,因为求值顺序没有规定,怎么样都有可能,这样的代码即使语法毫无问题,他也是错误的!

    所以有两条经验准则用于书写复合表达式:

    1. 拿不准的时候最好用括号来强制让表达式的组合关系符合程序逻辑的要求。

    2. 如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象

    OK,到目前为止,我似乎还没说多少关于条件运算符。接下来,我们用以上了解到的内容来看一看条件运算符。

    条件运算符是右结合的

    上面的规定是毫无疑问的,那么按照上面的知识来理解,对于 a ? b : c ? d : e按照右结合来解读先运算c,然后返回d或者e,返回后再参与到a ? b : d/e;是这样吗?

    很显然不是,为什么?开头前面的例子都是左结合从左边开始计算,右结合从右边开始计算,为什么这个不是?

    原因在于:前面的表达式中的运算符没有规定运算对象求值顺序,结合律只能在确定结合对象和计算方向后,按照结合性来计算表达式。但有四个是特例,这四个特殊的运算符规定了求值顺序和计算方向,它们分别如下:

    1. 逻辑与&&,先求左侧对象,左侧为真,再求右侧,左侧为假,则不再求右侧

    2. 逻辑或 || ,先求左侧对象,左侧为假,再求右侧,左侧为真,则不再求右侧

    3. 条件运算符 条件 ? 表达式1 : 表达式2 ,先对条件判断,为真,对表达式1进行计算,为假,对表达式2进行计算

    4. 逗号运算符“,”先求逗号运算符左侧的值,然后再对表达式右侧的求值。

    其中,第一条和第二条的求值策略,我们给它一个术语,叫做:short-circuit-evaluation(短路求值)。

    最后梳理一下,对于条件运算符,它是右结合的,对于 a ? b : c ? d : e这样的符合表达式,将最右边优先结合视为一个整体,相当于a ? b : (c ? d : e),但是并不是先对这个运算对象进行求值,如果没有规定求值顺序,可能先求b,也可能先求(c ? d : e),也可能先求a,然后再把a或b的最终结果或者(c ? d : e)的最终结果拿来从右向左开始参与表达式运算。也即运算对象求值不知道谁优先,但是表达式计算方向却是从右先左的。

    但是条件运算符规定了求值的顺序和计算方向必须先求条件a,然根据a的真假来求b或者(c ? d : e)。因此这里的右结合只起了怎么组合该复合表达式的作用,最终的求值顺序和表达式计算方向被该运算符的规定指明了。

  • 相关阅读:
    函数式编程
    橡皮筋功能
    socket
    git命令补充说明
    参考接口文档完成的json数据
    接口文档怎么写
    使用json-server创建mock数据
    proxy服务器代理
    Cannot read property 'setState' of undefined错误分析
    使用ref报错,addComponentAsRefTo(...): Only a ReactOwner can have refs.
  • 原文地址:https://www.cnblogs.com/pluse/p/5685844.html
Copyright © 2020-2023  润新知