简体   繁体   English

将增量/减量运算符放在三元/条件运算符中是否安全?

[英]Is it safe to put increment/decrement operators inside ternary/conditional operators?

Here's an example 这是一个例子

#include <iostream>
using namespace std;
int main()
{   
    int x = 0;
    cout << (x == 0 ? x++ : x) << endl; //operator in branch
    cout << "x=" << x << endl;
    cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition
    cout << "x=" << x << endl;
    return 0;
}

output: 输出:

0
x=1
1
x=1

I understand the output, but is this undefined behaviour or not? 我理解输出,但这是不确定的行为? Is the order of evaluation guaranteed in either case? 在任何一种情况下,评估顺序是否得到保证?

Even if guaranteed, I'm quite aware using increment/decrement can quickly become an issue for readability. 即使有保证,我也非常清楚使用递增/递减会很快成为可读性的问题。 I only ask as I saw similar code and was immediately unsure, given there are lots of examples of ambiguous/undefined use of increment/decrement operators, such as... 我只是问我看到类似的代码并且立即不确定,因为有很多关于递增/递减运算符的模糊/未定义使用的示例,例如......

  • C++ does not define the order in which function parameters are evaluated. C ++没有定义评估函数参数的顺序。

     int nValue = Add(x, ++x); 
  • The C++ language says you cannot modify a variable more than once between sequence points. C ++语言表示你不能在序列点之间多次修改变量。

      x = ++y + y++ 
  • Because increment and decrement operators have side effects, using expressions with increment or decrement operators in a preprocessor macro can have undesirable results. 因为递增和递减运算符具有副作用,所以在预处理器宏中使用具有递增或递减运算符的表达式可能会产生不良结果。

      #define max(a,b) ((a)<(b))?(b):(a) k = max( ++i, j ); 

For the conditional operator (§5.16 [expr.cond]/p1): 对于条件运算符(§5.16[expr.cond] / p1):

Every value computation and side effect associated with the first expression is sequenced before every value computation and side effect associated with the second or third expression. 在与第二或第三表达式相关联的每个值计算和副作用之前,对与第一表达式相关联的每个值计算和副作用进行排序。

For the logical OR operator (§5.15 [expr.log.or]/p1-2): 对于逻辑OR运算符(§5.15[expr.log.or] / p1-2):

the second operand is not evaluated if the first operand evaluates to true . 如果第一个操作数的计算结果为true则不计算第二个操作数。 [...] If the second expression is evaluated, every value computation and side effect associated with the first expression is sequenced before every value computation and side effect associated with the second expression. [...]如果计算第二个表达式,则在每个值计算和与第二个表达式相关的副作用之前,对与第一个表达式相关联的每个值计算和副作用进行排序。

The behavior of your code is well-defined. 代码的行为是明确定义的。

There is a guaranteed order of execution in ternary operators and boolean && and || 在三元运算符中有保证的执行顺序和boolean &&|| operations, so there is no conflict in evaluation sequence points. 操作,因此评估序列点没有冲突。

One at a time 一次一个

 cout << (x == 0 ? x++ : x) << endl; //operator in branch

Will always output x but will increment it only if it was 0. 将始终输出x但仅在它为0时才会递增。

 cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition

This is well defined too, if x was 1 it will not evaluate the RHS, if it wasn't it will decrement it but --x will never be 0, so it will be true iff x==1, in which case x will also now be 0. 这个定义也很明确,如果x为1,它将不会评估RHS,如果不是它将减少它但--x将永远不会为0,所以如果f = = 1则为真,在这种情况下x现在也将是0。

In the latter case if x is INT_MIN it is not well-defined behaviour to decrement it (and it would execute). 在后一种情况下,如果x是INT_MIN那么递减它就不是明确定义的行为(并且它将执行)。

That can't happen in the first case where x won't be 0 if it is INT_MAX so you are safe. 如果它是INT_MAX那么在x不会为0的第一种情况下不会发生这种情况,因此您是安全的。

I understand the output, but is this undefined behaviour or not? 我理解输出,但这是不确定的行为?

Code is perfectly defined. 代码完美定义。 C11 standard says: C11标准说:

6.5.15 Conditional operator 6.5.15条件运算符

The first operand is evaluated; 第一个操作数被评估; there is a sequence point between its evaluation and the evaluation of the second or third operand (whichever is evaluated). 在其评估与第二或第三操作数的评估之间存在一个序列点 (以评估者为准)。 The second operand is evaluated only if the first compares unequal to 0 ; 仅当第一个操作数不等于0时才评估第二个操作数; the third operand is evaluated only if the first compares equal to 0 ; 仅当第一个操作数比较等于0时才评估第三个操作数; the result is the value of the second or third operand (whichever is evaluated), converted to the type described below.110) 结果是第二个或第三个操作数的值(无论哪个被评估),转换为下面描述的类型.110)

6.5.14 Logical OR operator 6.5.14逻辑OR运算符

Unlike the bitwise | 与按位|不同 operator, the || 运算符, || operator guarantees left-to-right evaluation ; 运营商保证从左到右的评估 ; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands . 如果计算第二个操作数, 则在第一个和第二个操作数的计算之间存在一个序列点 If the first operand compares unequal to 0 , the second operand is not evaluated. 如果第一个操作数比较不等于0 ,则不计算第二个操作数。

Further wiki explains it with example: 进一步维基用例子解释:

  • Between evaluation of the left and right operands of the && (logical AND), || 在评估&& (逻辑AND)的左右操作数之间, || (logical OR) (as part of short-circuit evaluation), and comma operators . (逻辑OR)(作为短路评估的一部分)和comma operators For example, in the expression *p++ != 0 && *q++ != 0 , all side effects of the sub-expression *p++ != 0 are completed before any attempt to access q . 例如,在表达式*p++ != 0 && *q++ != 0 ,子表达式*p++ != 0所有副作用都在任何尝试访问q之前完成。

  • Between the evaluation of the first operand of the ternary "question-mark" operator and the second or third operand. 在评估三元“问号”运算符的第一个操作数和第二个或第三个操作数之间。 For example, in the expression a = (*p++) ? (*p++) : 0 例如,在表达式a = (*p++) ? (*p++) : 0 a = (*p++) ? (*p++) : 0 there is a sequence point after the first *p++ , meaning it has already been incremented by the time the second instance is executed. a = (*p++) ? (*p++) : 0在第一个*p++之后有一个序列点,这意味着它已经在第二个实例执行时增加了。

The rule for || ||的规则 and ?: is same for C++ (section 5.15 and 5.16) as in C. ?: C ++(第5.15和5.16节)与C相同


Is the order of evaluation guaranteed in either case? 在任何一种情况下,评估顺序是否得到保证?

Yes. 是。 The order of evaluation of the operands of operators || 运算符||的操作数的评估顺序 , && , , and ?: is guaranteed to be from left to right. &&,?:保证是由左到右。

In C an object's stored value can be modified only once between two sequence points. 在C中,对象的存储值只能在两个序列点之间修改一次。

A sequence point occurs: 出现一个序列点:

  1. At the end of full expression. 在完整表达结束时。
  2. At the && , || &&|| and ?: operators ?:运营商
  3. At a function call. 在函数调用。

So for example this expression x = i++ * i++ is undefined , whereas x = i++ && i++ is perfectly legal . 因此,例如,这个表达式x = i++ * i++未定义的 ,而x = i++ && i++完全合法的

Your code shows defined behaviour . 您的代码显示已定义的行为

int x=0; int x = 0;

cout << ( x == 0 ? x++ : x) << endl; cout <<(x == 0?x ++:x)<< endl;

In the above expression x is 0 , so x++ will be executed, here x++ is post increment so it will output 0 . 在上面的表达式中, x0 ,所以x++将被执行,这里x ++是后递增的,所以它将输出0

cout << "x=" << x << endl; cout <<“x =”<< x << endl;

In the above expression as x now has value 1 so the output will be 1 . 在上面的表达式中, x现在的值为1因此输出将为1

cout << (x == 1 || --x == 0 ? 1 : 2) << endl; cout <<(x == 1 || --x == 0?1:2)<< endl;

Here x is 1 so the next condition is not evaluated( --x == 0 ) and the output will 1 . 这里x1因此不评估下一个条件( --x == 0 ),输出将为1

cout << "x=" << x << endl; cout <<“x =”<< x << endl;

As the expression --x == 0 is not evaluated the output will again be 1 . 由于未评估表达式--x == 0 ,输出将再次为1

Yes, it is safe to use the increment/decrement operators as you have. 是的,使用增量/减量运算符是安全的。 Here is what is happening in your code: 以下是代码中发生的情况:

Snippet #1 片段#1

cout << (x == 0 ? x++ : x) << endl; //operator in branch

In this snippet, you are testing if x == 0 , which is true . 在这个片段中,您正在测试x == 0 ,这是true Since it is true , your ternary expression evaluates the x++ . 因为它是true ,你的三元表达式会评估x++ Since you are using a post-increment here, the original value for x is printed to the standard output stream, and then x is incremented. 由于您在此处使用后增量,因此x的原始值将打印到标准输出流, 然后 x递增。

Snippet #2 小片#2

cout << (x == 1 || --x == 0 ? 1 : 2) << endl; //operator in condition

This snippet is a bit more confusing, but it still yields a predictable result. 这个片段有点混乱,但它仍然会产生可预测的结果。 At this point, x = 1 from the first snippet. 此时,第一个片段的x = 1 In the ternary expression, the conditional part is evaluated first; 在三元表达式中,首先评估条件部分; however, due to Short-Circuiting , the second condition, --x == 0 , is never evaluated. 但是,由于短路第二个条件 --x == 0 ,永远不会被评估。

For C++ the operators || 对于C ++运算符|| and && are the short-circuiting boolean operators for logical OR and logical AND respectively. &&分别是逻辑OR逻辑AND的短路布尔运算符。 When you use these operators, your conditions are checked (from left to right) until the final result can be determined. 使用这些运算符时,会检查您的条件(从左到右),直到确定最终结果。 Once the result is determined, no more conditions are checked. 确定结果后,不再检查条件。

Looking at snippet #2, your first condition checks if x == 1 . 查看代码片段#2,您的第一个条件检查x == 1 Since your first condition evaluates to true and you are using the logical OR, there is no need to keep evaluating other conditions. 由于您的第一个条件评估为true并且您正在使用逻辑OR,因此无需继续评估其他条件。 That means that --x == 0 is never executed . 这意味着--x == 0 永远不会执行


A quick side-note about short-circuiting: 关于短路的快速说明:

Short-circuiting is useful for increasing performance in your program. 短路对于提高程序性能非常有用。 Suppose you had a condition like this which calls several time-expensive functions: 假设您有这样的条件,它会调用几个耗时的函数:

if (task1() && task2())
{ 
    //...Do something...
}

In this example, task2 should never be called unless task1 completes successfully ( task2 depends on some data that is altered by task1 ). 在这个例子中, task2不应该被称为除非task1成功完成( task2取决于是通过改变一些数据task1 )。

Because we are using a short-circuiting AND operator , if task1 fails by returning false , then if-statement has the sufficient information to exit early and stop checking other conditions. 因为我们使用的是短路AND运算符 ,如果task1因返回false失败,那么if-statement有足够的信息提前退出并停止检查其他条件。 This means that task2 is never called. 这意味着永远不会调用task2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM