if ( ) + side effects

Hi together,

in a book I came across examples that demonstrate the behavior of if-structures when the condition expression has side effects. The author claims that first the condition is evaluated to True or False, then the side effect is applied, eg.
1
2
3
int a = 0;
if(a++){cout<<"TRUE"<<endl;}
else{cout<<"FALSE"<<endl;}

will print out "FALSE".

Yet, when one uses
1
2
3
int a = 0;
if(++a){cout<<"TRUE"<<endl;}
else{cout<<"FALSE"<<endl;}

one gets "TRUE", which means that the side effect was performed before the evaluation, wasn't it?

Also, how do I know what a side effect is? For example:
1
2
3
4
5
int a = 0;
int b=1;

if(a=b){cout<<"TRUE"<<endl;}
else{cout<<"FALSE"<<endl;}

will give "TRUE" because first b is written to a and then the whole thing is evaluated. This means that apparently the = operator is no side effect, in contrast to ++. Because if it was, I would assume that first "a" gets evaluated, the condition gives "FALSE", and then the assignment happens.

Some insight?

Best,
PIF
Last edited on
Hmm, I think it might be better to not think of an if statement as being a special case. The statement within an if condition is evaluated in the same way that an assignment or any other expression is evaluated.

(1)
1
2
3
int a = 0;
if(a++){cout<<"TRUE"<<endl;}
else{cout<<"FALSE"<<endl;}

is equivalent to
1
2
3
4
int a = 0;
int temp = a++;
if(temp){cout<<"TRUE"<<endl;}
else{cout<<"FALSE"<<endl;}


(2) Likewise,
1
2
3
int a = 0;
if(++a){cout<<"TRUE"<<endl;}
else{cout<<"FALSE"<<endl;}

is equivalent to
1
2
3
4
int a = 0;
int& temp = ++a;   // (edit: fixed)
if(temp){cout<<"TRUE"<<endl;}
else{cout<<"FALSE"<<endl;}


(3) And to drill the concept in further,
1
2
3
4
5
int a = 0;
int b=1;

if(a=b){cout<<"TRUE"<<endl;}
else{cout<<"FALSE"<<endl;}

is equivalent to
1
2
3
4
5
6
7
int a = 0;
int b=1;

int& temp = a = b;

if(temp){cout<<"TRUE"<<endl;}
else{cout<<"FALSE"<<endl;}


A 'side effect' is anything that isn't the return value of the expression.

a=b is an expression that assigns the value of 'b' to 'a' (side effect). It returns a reference to the left-hand side of the expression, 'a' (return value).

a++ is an expression that increments the value of 'a' by one (side effect), but returns a copy of 'a' before it was incremented (return value).

++a is an expression that increments the value of 'a' by one (side effect), and then returns the now-current value of 'a', as a reference (return value).

So you can think of any expression as something that has side <side effects> and its <return value is used or evaluated in the if condition>.

If you separate the result (return value) of an expression, from the side effect of an expression, it might help clear the confusion.

___________________

Another example: (cout <<" hello") is an expression that returns a reference to the cout stream. Its side effect is that "hello" is printed to the screen. The side effect happens before the return value can be used further down, e.g. std::ostream& cout_ref = (std::cout << "hello");
Last edited on
@Gando - I have to disagree with you. The first snippet the OP posted does indeed print FALSE, meaning a was evaluated first (0), then incremented. The second snippet you posted is not equivalent.

1
2
3
4
5
6
7
8
9
10
#include <iostream>

int main()
{
	int a = 0;
	if (a++) 
	{	std::cout << "TRUE" << std::endl; }
	else 
	{	std::cout << "FALSE" << std::endl; }
}


FALSE

Last edited on
@AbstractionAnon - I'm not quite sure what you're disagreeing with for the first snippet. Of course that prints "FALSE". Just as the following does:
1
2
3
4
5
6
7
8
9
#include <iostream>
using namespace std;
int main()
{
    int a = 0;
    int temp = a++;
    if(temp){cout<<"TRUE"<<endl;}
    else{cout<<"FALSE"<<endl;}
}

FALSE


Edit: I sort of understand where the disagreement is. And after thinking about it, I agree with your wording. But I'm trying not to get bogged down by standardese, that I don't even fully understand myself.
https://en.cppreference.com/w/cpp/language/eval_order
From what I understand, it boils down to five points:
- "Evaluation" includes both the value computation and the side effect.
- If A is sequenced before B, then evaluation of A will be complete before evaluation of B begins.
- The value computation of the built-in post-increment and post-decrement operators is sequenced before its side-effect.
- The side effect of the built-in pre-increment and pre-decrement operators is sequenced before its value computation (implicit rule due to definition as compound assignment).
- The side effect (modification of the left argument) of the built-in assignment operator and of all built-in compound assignment operators is sequenced after the value computation (but not the side effects) of both left and right arguments, and is sequenced before the value computation of the assignment expression (that is, before returning the reference to the modified object).

But the above is really wordy and confusing. So my point is that regardless of preincrement, postincrement, or assignment, you can think of all of them as just being a function, where a function has side effects that happen within it, and then a return value.

I fixed the second snippet (forgot the reference), thank you. I updated my original post to try to clarify what I wrote. Sorry for the confusion. I believe my code snippets are valid, and are a nice generalized way of looking at things.
Last edited on
The C++ standard orders "value computation" and "initiation of side effects" differently for each expression. So for built-in pre-increment the standard says that side effects happen before the value of the expression is figured out. And for built-in post-increment the standard says that the value of the expression is figured out before side effects.

It probably doesn't matter which mental model you prefer.
Last edited on
@Gando - I didn't believe you until I tried it myself just now. The second snippet does indeed print FALSE. My understanding was that when the semicolon was reached on line 6 (of the program in your last post), all side effects have been applied.

edit: I don't know what I was thinking. Of course the assignment on line 6 is done first, before the post increment.
Last edited on
An if..else statement has THREE full evaluation contexts:

  ① The condition in parentheses following the “if” keyword
  ② The statement or statement block following the parentheses
  ③ The statement or statement block following the “else” keyword

The first evaluation context is completely evaluated before considering evaluation of the second or third.

Were this not so, an IF statement would be pretty close to useless.
Hi everyone, thank you for your elaborate answers.

@Ganado,
these are very clear examples, and mentally distinguishing between returned value of an expression and its side effect really helps. One question: Why do you write your temp variable for the pre-increment as a reference but not for the post-increment?

Also, from what you said in your second post and from what @mbozzi said, it seems to be clear now that the author worded it wrong. Side effects within the if ( ) brackets are not always applied before the value of the expression is returned, and it indeed depends on the particular expression.
edit: I just reread the passage, and in his examples, the author only used the post-increment operator, so maybe his statements were only referring to this case.
Last edited on
https://www.teamten.com/lawrence/programming/keep-if-clauses-side-effect-free.html
The only function of an if statement is to test whether a condition is true. It’s not for executing code as a side-effect of the test. One problem with using the return value directly, as in the above, is that the meaning of the returned value is unclear.


Just as @Duthomas has written, the if statement requires nothing elaborate at all. In fact anything elaborate here is BS and bad programming.
I understand that it is probably bad practice to have side effects in if conditions. This questions is simply about how the language works.
Last edited on
One question: Why do you write your temp variable for the pre-increment as a reference but not for the post-increment?
The purpose of that was to match the actual type of the return value of those expressions. It doesn't actually affect the behavior of these particular examples, but I wanted the code to be 1:1.
- pre-increment return a reference the current value
- post-increment returns a copy to the previous value
- assignment also returns a reference to the LHS of the assignment.

And correct, what mbozzi said is the more accurate way of putting it.
Last edited on
All good but the point is you won’t get side effects if you use the language the way it is designed. Combine that with writing easily read and maintainable code and the chances are you will be a good programmer rather than an elaborate BS’er.

The side effect of using a chainsaw to brush your teeth is a very elaborate facelift.
Thank you Ganado, I understand!

I used what you wrote to the assignment operator in a new question to undefined behavior.

- The side effect (modification of the left argument) of the built-in assignment operator and of all built-in compound assignment operators is sequenced after the value computation (but not the side effects) of both left and right arguments, and is sequenced before the value computation of the assignment expression (that is, before returning the reference to the modified object).


http://www.cplusplus.com/forum/beginner/280611/
Side effects are part of the language.


Functions and expressions have a primary effect — they directly compute a value. The C++ Standard calls this a “value computation”.

    a*a + b*b - 7;  // I'm a "value computation"
    floor( 12.8 );  // Me too

Any observable effect on the “execution environment” other than the primary effect of the function or expression is called a side effect.

 ⟶ The execution environment is the state of the computer as viewed by the C++ language.
      There are plenty of things that change behind the scenes that you don’t have to care about,
      and which you do not need to consider a side effect.


For example, the expression n++ has both

  • a primary effect: return the value of n
  • a side effect: bump the value of n after computing the primary effect

Both effects are wholly intentional and designed as part of the language.


The conditional part of an if statement, however, does not care about side effects, no matter how intentional. The only thing that matters is the primary effect and whether the result of that primary effect can be treated as a boolean true or false value.

And again, the entire value computation (in parentheses following the if keyword) is fully sequenced (AKA fully computed) before any consideration is given to the then or else clauses of the if statement.
@Duthomhas

While I appreciate this explanation, I think you misunderstood the question. It was about side effects within the conditional part, eg. if(++n). But I think I get it now, whether the side effect happens before or after the value computation depends on the particular operation.
I think you have misunderstood the answer. Everything in the parentheses is a single value computation; any side effects occurring there mean nothing if they do not affect the value computation.

A pre-increment operator’s side effect is applied before the value computation, hence the printing of “TRUE”.

Further, you explicitly asked “how do I know what a side effect is?” and “Some insight?”

In what way has my answer (plus my answer in your other thread) not directly addressed those questions?

The conditional part is one complete value computation sequence. So yes, pre-increment and pre-decrement occur before the computation; everything else occurs either some time during the computation (such as when calling sqrt()) or after the computation (such as when calling post-increment).
Last edited on
This questions is (sic) simply about how the language works.
Really?
Topic archived. No new replies allowed.