[转载][奇怪の东西]关于 C++ 中三目运算符的说明

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

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

  对于第三个例子,是两个优先级相同,但是是不同的运算符,根据结合律从左向右结合,进行计算。这里就会出现 2 个名词,一个是表达式的计算顺序,或者叫表达式的计算方向,另一个叫运算对象的求值顺序。在第三个例子当中,由于是左结合的,因此表达式的计算方向是从左往右。但是毕竟 f( )g( ) 是两个函数,参与运算的是函数的返回值,而不是函数,因此需要事先对这两个函数进行调用,调用完毕后,将返回值拿过来参与运算,假设f( )和g( )的返回值分别是 fg,即最终参与表达式运算的是 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,然后再把 ab 的最终结果或者 (c? d: e) 的最终结果拿来从右向左开始参与表达式运算。也即运算对象求值不知道谁优先,但是表达式计算方向却是从右先左的。

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

posted @ 2022-02-05 22:27  Arextre  阅读(84)  评论(0编辑  收藏  举报