Velocity Reviews > short-circuit evaluation and assignment operators

# short-circuit evaluation and assignment operators

Anthony Paul
Guest
Posts: n/a

 06-06-2009
Hello everyone,

I have the following code for testing my expression evaluator (I know
the code doesn't make sense, but it doesn't have to for the purposes
of this test) :

int Z = 5;
int e = Z || Z <<= Z;

My evaluator first evaluates Z, sees that it's true, and doesn't
evaluate the Z <<= Z part. So far so good. Now if I set Z = 0, it
would check Z, find it to be false, and then evaluate Z <<= Z and find
that false as well.

When I test the same code on either gcc or Visual Studio's C++
compiler, I get the "error: lvalue required as left operand of
assignment" error message during compilation. This would make sense if
Z || Z was evaluated first, and then it tried to apply Z <<= Z to the
result of that evaluation, but that can't be now can it? The short-
circuit evaluator clearly separates the expression into two camps :

Camp A : Z
||
Camp B : Z <<= Z

The above should clearly not produce the lvalue required error
message; Z IS an lvalue.

And yet the only possible way I can explain its reasoning is if it
were evaluating the expression as follows :

Camp A : Z || Z
Camp B : (result of Z || Z) <<= Z

But if that's the case, how could it possibly do short-circuit
evaluation?

Strange...

Warm regards,

Anthony

Peter Nilsson
Guest
Posts: n/a

 06-06-2009
Anthony Paul wrote:
> int Z = 5;
> int e = Z || Z <<= Z;

<snip>
> When I test the same code on either gcc or Visual Studio's C++
> compiler, I get the "error: lvalue required as left operand of
> assignment" error message during compilation. This would
> make sense if Z || Z was evaluated first,

It's a matter of parsing, not evaluation. Whilst Bitwise Shift
Operators
have higher precedence than Logical OR, Assignment operators
have a lower precedence.

> ... the only possible way I can explain its reasoning is if it
> were evaluating the expression as follows :
>
> Camp A : Z || Z
> Camp B : (result of Z || Z) <<= Z
>
> But if that's the case, how could it possibly do short-circuit
> evaluation?

It can't. Write it as...

e = Z || (Z <<= Z);

--
Peter

luserXtrog
Guest
Posts: n/a

 06-06-2009
On Jun 6, 12:03*am, Anthony Paul <(E-Mail Removed)> wrote:
> Hello everyone,
>
> I have the following code for testing my expression evaluator (I know
> the code doesn't make sense, but it doesn't have to for the purposes
> of this test) :
>
> int Z = 5;
> int e = Z || Z <<= Z;
>
> My evaluator first evaluates Z, sees that it's true, and doesn't
> evaluate the Z <<= Z part. So far so good. Now if I set Z = 0, it
> would check Z, find it to be false, and then evaluate Z <<= Z and find
> that false as well.
>
> When I test the same code on either gcc or Visual Studio's C++
> compiler, I get the "error: lvalue required as left operand of
> assignment" error message during compilation. This would make sense if
> Z || Z was evaluated first, and then it tried to apply Z <<= Z to the
> result of that evaluation, but that can't be now can it? The short-
> circuit evaluator clearly separates the expression into two camps :
>
> Camp A : Z
> ||
> Camp B : Z <<= Z
>
> The above should clearly not produce the lvalue required error
> message; Z IS an lvalue.
>
> And yet the only possible way I can explain its reasoning is if it
> were evaluating the expression as follows :
>
> Camp A : Z || Z
> Camp B : (result of Z || Z) <<= Z
>
> But if that's the case, how could it possibly do short-circuit
> evaluation?
>
> Strange...
>
> Warm regards,

Assignments have lower precedence that logical operators.
This includes <<=. So the expression is parsed like your
second example. Like this:

e = ((Z || Z) <<= Z);

Now if the first Z is non-zero, it most certainly could avoid
evaluating the second Z. But the result of the logical operator
is not something that can be stored into.

You'll need explicit parens to explain this to the compiler.
The default rules interpret your expression as nonsense.
You could try:

e = (Z || (Z <<= Z));

But, of course, since the right hand size is short-circuited
unless Z == 0, what's the sense in rotating 0 zero times?

--
lxt

Anthony Paul
Guest
Posts: n/a

 06-06-2009
Hey guys,

I see both of your points, and I think I understand now but I want to
clarify. Are you guys saying that whether short-circuit evaluation is
performed depends on precedence? If so, I get what you're saying.

To make sure I got this straight, the following :

Z || Z <<= Z

is evaluated as as

(Z || Z) <<= Z

and that's because the <<= operator has lower precedence. I imagine
that the short-circuit happens between the two Z's now, it evaluates
the first Z and finds it to be true (1) and doesn't evaluate the
second Z. It then does 1 <<= Z which is obviously an error. Got it.

Now in the case of :

Z || Z / 0

This compiles fine and evaluates to a 1. I would imagine that it is
evaluating this as :

Z || (Z / 0)

because the division operator has higher precedence. Now the short-
circuit happens completely different from the first example.... it's
now between the Z and the (Z / 0). Since the first Z evaluates to true
(1) it doesn't bother doing the rest.

If what I just said makes sense, then I understand now and I thank you
both! If not, then God help me!

Regards,

Anthony

Barry Schwarz
Guest
Posts: n/a

 06-06-2009
On Fri, 5 Jun 2009 23:02:50 -0700 (PDT), Anthony Paul
<(E-Mail Removed)> wrote:

>Hey guys,
>
>
>I see both of your points, and I think I understand now but I want to
>clarify. Are you guys saying that whether short-circuit evaluation is
>performed depends on precedence? If so, I get what you're saying.

No, short circuit evaluation does not depend of precedence. The short
circuit operators always perform short circuit evaluations.

What depends on precedence is which parts of the expression are the
actual operands of the || operator. You thought one operand was Z and
the other was Z<<=Z. Due to the precedence, the left operand was the
first Z and the right operand was the second Z.

There is no language problem with this. The problem occurs because
the result of Z||Z was used as the left operand of the <<= operator.

You got it right below.

>
>To make sure I got this straight, the following :
>
>Z || Z <<= Z
>
>is evaluated as as
>
>(Z || Z) <<= Z
>
>and that's because the <<= operator has lower precedence. I imagine
>that the short-circuit happens between the two Z's now, it evaluates
>the first Z and finds it to be true (1) and doesn't evaluate the
>second Z. It then does 1 <<= Z which is obviously an error. Got it.
>
>Now in the case of :
>
>Z || Z / 0
>
>This compiles fine and evaluates to a 1. I would imagine that it is
>evaluating this as :
>
>Z || (Z / 0)
>
>because the division operator has higher precedence. Now the short-
>circuit happens completely different from the first example.... it's
>now between the Z and the (Z / 0). Since the first Z evaluates to true
>(1) it doesn't bother doing the rest.

--
Remove del for email

gw7rib@aol.com
Guest
Posts: n/a

 06-06-2009
On 6 June, 07:02, Anthony Paul <(E-Mail Removed)> wrote:
> Hey guys,
>
>
> I see both of your points, and I think I understand now but I want to
> clarify. Are you guys saying that whether short-circuit evaluation is
> performed depends on precedence? If so, I get what you're saying.

Barry Schwarz has commented on this.

> To make sure I got this straight, the following :
>
> Z || Z <<= Z
>
> is evaluated as as
>
> (Z || Z) <<= Z
>
> and that's because the <<= operator has lower precedence. I imagine
> that the short-circuit happens between the two Z's now, it evaluates
> the first Z and finds it to be true (1) and doesn't evaluate the
> second Z. It then does 1 <<= Z which is obviously an error. Got it.
>
> Now in the case of :
>
> Z || Z / 0
>
> This compiles fine and evaluates to a 1. I would imagine that it is
> evaluating this as :
>
> Z || (Z / 0)
>
> because the division operator has higher precedence. Now the short-
> circuit happens completely different from the first example.... it's
> now between the Z and the (Z / 0). Since the first Z evaluates to true
> (1) it doesn't bother doing the rest.

Yes, these examples look fine.

Part of the problem is that computing is not the same as maths,
despite using similar terms. For instance, in maths, if you do:

x = 3 + 4 * 5

then to say "the addition uses the result of the multiplication" is
much the same thing as saying "you do the multiplication first".
However, these are slightly different concepts in computing. The
question of which operators operate on which values is called
precedence (unless you're very pedantic). The question of which
calculation is done when is called order of execution.

Here's a particularly tricky example:

x = y || (4+5);

In this case, the brackets give the addition a higher precedence
(though they're not strictly needed as + has a higher precedence than
|| anyway). So the values that || acts on are, on the one hand, y, and
on the other hand, 4+5. That's precedence. BUT, || works by checking
the first value and only evaluating the second value if needed - so
here, if y is not zero, 4 and 5 aren't added at all. So || starts
operating *before* the addition is carried out.

Once you follow that, everything else will be a walk in the park...

Hope that helps.
Paul.