In an if statement with multiple conditionals, is the second conditional executed if the outcome of the first is clear?
example:
if(i>0 && array[i]==0){
}
If I swap the conditionals a segfault may occur for negative values of i, but this way no segfault occurs. Can I be sure that this always works or do have have to use nested if statements?
This type of evaluation is called short-circuiting.
Once the result is 100% clear, it does not continue evaluating.
This is actually a common programming technique.
For example, in C++ you will often see something like:
if (pX!=null && pX->predicate()) { bla bla bla }
If you changed the order of the conditions, you could be invoking a method on a null pointer and crashing. A similar example in C would use the field of a struct when you have a pointer to that struct.
You could do something similar with or:
if(px==null || pX->isEmpty()} { bla bla bla }
This is also one of the reasons that it is generally a good idea to avoid side effects in an if condition.
For example suppose you have:
if(x==4 && (++y>7) && z==9)
If x is 4, then y will be incremented regardless of the value of z or y, but if x is not 4, it will not be incremented at all.
The operators && and || guarantee that the left-hand side expression will be fully evaluated (and all side effects applied) before the right-hand side is evaluated. In other words, the operators introduce a sequence point.
Additionally, if the value of the expression can be determined from the lhs, the rhs is not evaluated. In other words, if you have an expression like x && y, and x evaluates to 0 (false), then the value of the expression is false regardless of y, so y is not evaluated.
This means that expressions like x++ && x++ are well-defined, since && introduces a sequence point.
From draft 3485 (n3485.pdf) Its clearly stated that
5.14 Logical AND operator [expr.log.and]
logical-and-expression:
inclusive-or-expression
logical-and-expression && inclusive-or-expression
The && operator groups left-to-right. The
operands are both contextually converted to bool (Clause 4). The
result is true if both operands are true and false otherwise. Unlike
&, && guarantees left-to-right evaluation: the second operand is not
evaluated if the first operand is false.
The result is a bool. 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.
Related
This question already has answers here:
What is short-circuit evaluation in C?
(3 answers)
Closed 2 years ago.
Consider the following code:
int y, x; y = 2; x = 0;
x!=0&&y/x>5
My lecturer's textbook on C provides a table for "Precedence hierarchy for the arithmetic, relational, and logical operators" using this table to evaluate the operators in the above expression I get the following precedence:
(1) is /, therefore, do y/x first
(2) is >, (3) is !=, (4) is &&.
If I evaluate the above code using this precedence hierarchy, then the first subexpr to be evaluated is:
y / x
which is
2 / 0, or undefined...
The given answer is 0.
Later on, the textbook also states that, per the ANSI C standard, the operand on the left of && will always be evaluated first, and only if this is 0 will the operand on the right be evaluated.
Does this mean that whenever a logical and (&&) (or a logical or (||) for that matter) appears in an expression along with arithmetic and relational operators, that effectively I need to separate the terms of the overall expression into two groups - terms on the left, and terms on the right, of the &&, and ONLY THEN begin applying the "Precedence hierarchy for the arithmetic, relational, and logical operators" to the operators contained in the overall 'left hand operand'?
This would result in the above expression being evaluated as:
(x!=0) && (y/x>5)
starting with the left operand, (x!=0), which is (0!=0), which is false, so 0, and thus no further evaluation takes place.
--
If this is the case, why does the && operator appear so low down the hierarchy of precedence if its inclusion in an expression dictates what must be done first?
In this case, you're encountering something called "short circuit evaluation". Think of your condition if (x != 0 && y / x > 5) as if (somethingIsTrue && somethingIsTrue). In order for that to be true, it must become if (true && true). In short circuit evaluation, it sees that x !=0 is false and immediately stops evaluating there because no matter what comes after it, the first thing is false so it can never be true.
And yes, to your point, you can sort of think of it as splitting the expressions into groups between the && and || statements. Each of those individual expressions gets evaluated as true or false, and then just look at it as a bunch of true or false statements in between && and || statements. So, for something like if (blah1 && blah2 || blah3), no matter what blah1, blah2, and blah3 are, they will all get evaluated to true or false. And then you just see how it plays out like if (true && true || false) or something like that.
And on a side note, don't bang your head against a wall trying to memorize precedence rules. Most of them are intuitive and you'll get the hang of the correct way of doing things as you program more.
The precedence of operator makes clear how your expression x!=0&&y/x>5 is to be interpreted. Think about it as setting braces. So the compiler would read your expression as (x!=0) && ((y/x)>5). Note here that the operators with the highest precedence are getting braces first, the ones with lower precedence are getting braces later.
Now the evaluation takes place. Evaluation is done from outer to inner. The outmost operator is the one with the lowest priority, the &&. It has two operands, the left one and the right one. Now the standard says that the left one is to be evaluated first. If it is false, then the && returns false (shortcut). If it is true, then the right operand needs to be evaluated.
The good thing about this guaranteed evaluation order is that you can now safely write for example if (x!=0 && y/x>10) or if (pointer!=NULL && pointer->data != 3).
Precedence and associativity only say how to structure an expression. They do not say how to evaluate it. Consider x+y*z. Were it not for precedence, we could structure this expression as (x+y)*z, shown on the left, or x+(y*z), shown on the right.
* +
/ \ / \
+ z x *
/ \ / \
x y y z
Precedence tells us to use the latter. But we can still evaluate x, y, and z in any order. Suppose x, y, and z are actually function calls with side effects; perhaps they print “x”, “y”, and “z”. If we evaluate z and remember its result, then evaluate y, then multiply them, then evaluate x, and then add, we will get the same result as if we evaluate x, then y, then z, then multiply, then add.
Both of these orders of evaluation use the same expression structure, the latter one. So the precedence that gave us the expression structure did not tell us how to evaluate the expression. (Almost—the structure does compel us to get the result of the multiplication before we can use it in the addition.)
The && and || operators have a property beyond precedence: They have a rule that their left operand is evaluated first, and the right operand is evaluated only if the left operand does not determine the result.
Digging deeper, there are two computations associated with some expressions. Each expression has its main effect: to produce a value, such as the sum, product, or logical AND of its operands. Some expressions also have side effects, such as incrementing an object (in x++) or writing output (putchar('c')). The side effect does not have to be done at the same time as the main effect. Generally, it can be done any time during the full expression it is in, and it can be before, during, or after the main effect.
The ordering property of the && and || operators extends to side effects: They require all side effects of their left operand be completed before any part of the right operand is evaluated.
Can anybody explain why the output is 'a' only, if && has higher priority than ||. Below is the code snippet.
#include<stdio.h>
int main(){
int a = printf("a")||printf("b")&&printf("c");
return 0;
}
This is a feature called "Short Circuit Evaluation".
The code is trying to determine if the expression X || Y && Z is True or False so it can assign a value to variable a.
Because of short-circuit evaluation, once the first part, printf("a"), is True (non-zero), it does not matter what the 2nd half of the expression is, so it does not get evaluated.
True || (*anything*) will result in true, so why bother evaluating the 2nd half of the code?
The entire 2nd part of the expression: printf("b")&&printf("c") gets skipped completely, because its not needed.
As your code demonstrates very well, when part of an expression is skipped due to Short Circuiting, any side-effects of that code (such as printing to the console) get skipped as well, sometimes leading to confusing and non-obvious bugs.
Not all languages have short-circuit evaluation, but it is commonly found in most languages.
&& has higher precedence, according to conventional terminology. "Priority" gives a potentially (more) misleading impression.
Whatever you call it, it is not about order of operations, but rather about correctly identifying the operands of each operator. That && has higher precedence means that your declaration of a is equivalent to
int a = printf("a") || (printf("b")&&printf("c")); // THIS
, as opposed to
int a = (printf("a")||(printf("b")) && printf("c"); // NOT THIS
Furthermore, the printf("a") is executed first either way, because the left-hand operand of && and || is always evaluated before the right-hand one, and the right-hand operand is only evaluated at all if that is necessary to determine the result of the operation.
In your case, each printf call will return 1 on success (if it is executed at all). The printf("a") is executed first, per the rule for the operands of an || operator. Its result is enough to determine the overall result of that operation, so no part of the other operand is evaluated.
If you used parentheses to override the default grouping then the short-circuiting would be more narrowly scoped, with the result that ac would be printed.
Precedence only affects which operators are grouped with which operands, not the order in which subexpressions are evaluated.
Both && and || force left-to-right evaluation. Due to precedence rules, the expression x || y && z is parsed as x || (y && z), however it is evaluated as follows:
x is evaluated and all side effects are applied;
if x evaluates to 0, then
y is evaluated and all side effects applied
if y evaluates to non-zero, then
z is evaluated
if z is non-zero, then
the result of y && z is 1, and
the result of x || y && z is 1
else
fhe result of y && z is 0, and
the result of x || y && z is 0
end if
else
z is *not* evaluated
the result of y && z is 0, and
the result of x || y && z is 0
end if
else
y && z is *not* evaluated
x || y && z evaluates to 1
end if
Remember that both && and || short-circuit - depending on the value of the left operand, the right operand will not be evaluated. If a is 0, then a && b evaluates to 0 regardless of the value of b, so b is not evaluated at all.
Similarly, if a is non-zero, then a || b evaluates to 1 regardless of the value of b, so b is not evaluated.
1) Sequence Point
A sequence point defines any point in a computer program's execution
at which it is guaranteed that all side effects of previous
evaluations will have been performed, and no side effects from
subsequent evaluations have yet been performed.
At a sequence point, it is guaranteed that evaluation of all previous sub-expressions are complete.
2) From Microsoft article:
Only the sequential-evaluation (,), logical-AND (&&), logical-OR (||),
conditional-expression (? :), and function-call operators constitute
sequence points, and therefore guarantee a particular order of
evaluation for their operands.
So, there exists a sequence point between left operand and right operand of logical OR || and logical AND &&. Hence, logical operators guarantee evaluation of their operands from left to right.
3) From C Sequence Points
The left operand of the
logical-AND operator is completely evaluated and all side effects
complete before continuing. If the left operand evaluates to false
(0), the other operand is not evaluated.
The left operand of the
logical-OR operator is completely evaluated and all side effects
complete before continuing. If the left operand evaluates to true
(nonzero), the other operand is not evaluated.
4) Reason:
int a = printf("a") || printf("b") && printf("c");
So, there is a sequence point between printf("a") and (printf("b") && printf("c")).
That's why first printf("a") is evaluated. Since, the left operand result is non-zero, the right operand is not evaluated (short-circuit evaluation).
printf() is a function that always returns the number of written characters (for printf("a"); it returns 1).
The logical operators (|| and &&) in C evaluate their arguments from left to right in a way that they cut the evaluation at the point on which there's security on the final result. As the first argument to || is true (because 1 is different than 0), the right operand to || doesn't need to be evaluated, as the result will be true anyway, and this is why the right expression (printf("b") && printf("c")) is not evaluated at all.
For || if the left operand evaluation gives true, there's no need to evaluate the right argument. For && if the left operand evaluates false, then the result will be false, and there's no need to evaluate the right argument.
In an if statement with multiple conditionals, is the second conditional executed if the outcome of the first is clear?
example:
if(i>0 && array[i]==0){
}
If I swap the conditionals a segfault may occur for negative values of i, but this way no segfault occurs. Can I be sure that this always works or do have have to use nested if statements?
This type of evaluation is called short-circuiting.
Once the result is 100% clear, it does not continue evaluating.
This is actually a common programming technique.
For example, in C++ you will often see something like:
if (pX!=null && pX->predicate()) { bla bla bla }
If you changed the order of the conditions, you could be invoking a method on a null pointer and crashing. A similar example in C would use the field of a struct when you have a pointer to that struct.
You could do something similar with or:
if(px==null || pX->isEmpty()} { bla bla bla }
This is also one of the reasons that it is generally a good idea to avoid side effects in an if condition.
For example suppose you have:
if(x==4 && (++y>7) && z==9)
If x is 4, then y will be incremented regardless of the value of z or y, but if x is not 4, it will not be incremented at all.
The operators && and || guarantee that the left-hand side expression will be fully evaluated (and all side effects applied) before the right-hand side is evaluated. In other words, the operators introduce a sequence point.
Additionally, if the value of the expression can be determined from the lhs, the rhs is not evaluated. In other words, if you have an expression like x && y, and x evaluates to 0 (false), then the value of the expression is false regardless of y, so y is not evaluated.
This means that expressions like x++ && x++ are well-defined, since && introduces a sequence point.
From draft 3485 (n3485.pdf) Its clearly stated that
5.14 Logical AND operator [expr.log.and]
logical-and-expression:
inclusive-or-expression
logical-and-expression && inclusive-or-expression
The && operator groups left-to-right. The
operands are both contextually converted to bool (Clause 4). The
result is true if both operands are true and false otherwise. Unlike
&, && guarantees left-to-right evaluation: the second operand is not
evaluated if the first operand is false.
The result is a bool. 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.
I know that logical operators in C follow short circuiting but my doubt is that are short circuiting and operator precedence rules not opposing each other. See the below example :
#include<stdio.h>
int main()
{
int a;
int b=5;
a=0 && --b;
printf("%d %d",a,b);
return 0;
}
According to the precedence rules, the highest precedence is of the prefix operator. So --b should be evaluated first and then the && and at last result will be assigned to a. So expected output should be 0 4. But in this case the second operand of && never actually executes and result comes out to be 0 5.
Why precedence rules are not being applied here. Are logical operators exempted from precedence rules? If yes, what other operators show such behavior? And what is the logic behind this behavior?
You're conflating two related but different topics: operator precedence and order of evaluation.
The operator precedence rules dictate how various operators are grouped together. In the case of this expression:
a=0 && --b;
The operators are grouped like this:
a = (0 && (--b));
This has no effect however on which order the operands are evaluated in. The && operator in particular dictates that the left operand is evaluated first, and if it evaluates to 0 the right operand is not evaluated.
So in this case the left side of && which is 0 is evaluated, and because it is 0 the right side which is --b is not evaluated, so b is not incremented.
Here's another example of the difference between operator precedence and order of evaluation.
int val()
{
static x = 2;
x *= 2;
return x;
}
int main()
{
int result = val() + (5 * val());
printf("%d\n", result);
return 0;
}
What will the above program print? As it turns out, there are two possibilities, and both are valid.
In this expression:
val() + (5 * val())
There are no operators that have any type of short circuit behavior. So the compiler is free to evaluate the individual operands of both + and * in any order.
If the first instance of val() is evaluated first, the result will be 4 + ( 5 * 8) == 44. If the second instance of val() is evaluated first, the result will be 8 + (5 * 4) == 28. Again, both are valid since the operands may be evaluated in any order.
Precedence affects how ambiguous expressions are parsed. When there are multiple ways to interpret an expression with several operators, precedence tells us which interpretation is correct. Think of precedence as a mechanism to figure out where the implied parentheses are.
For example in the statement in question there are two valid ways to parse it. If = had higher precedence than && it could be read as:
(a = 0) && --b;
But since && has higher precedence, it's actually interpreted as:
a = (0 && --b);
(Note: Your code's formatting suggests it's the first. Be careful not to mislead!)
Evaluation order is different from precedence. They're related, but independent concepts. After precedence is used to determine the correct parsing of an expression, evaluation order tells us the order to evaluate the operands in. Is it left to right? Right to left? Simultaneous? Unspecified?
For the most part evaluation order is left unspecified. Operators like + and * and << have no defined evaluation order. The compiler is allowed to do whatever it likes, and the programmer must not write code that depends on any particular order. a + b could evaluate a then b, or b then a, or it could even interweave their evaluations.
= and &&, among others, are exceptions. = is always evaluated right to left, and && is left to right with short circuiting.
Here's how evaluation proceeds step-by-step for our statement:
a = (0 && --b), = evaluated right to left
0 && --b, && evaluated left to right with short circuiting
0, evaluates false which triggers short circuiting and cancels the next step
--b, not evaluated due to short circuiting
result is 0
a, variable reference evaluated
a = 0, assignment occurs and overall result is 0
You said that there is no specific order for + and *, but this table shows the order to be left to right. Why so?
The last column of that table is associativity. Associativity breaks precedence ties when we use the same operator twice, or when we use operators with the same precedence.
For example, how should we read a / b / c. Is it:
(a / b) / c, or
a / (b / c)?
According to the table / has left-to-right associativity, so it's the first.
What about chained assignments like foo = bar = baz? Now, assignment has right-to-left associativity, so the correct parsing is foo = (bar = baz).
If this all gets confusing, focus on one simple rule of thumb:
"Precedence and associativity are independent from order of evaluation."
Operator precedence doesn't necessarily tell that an expression gets executed first, it just means that the expression is parsed such that the result of the higher-precedence operation is used in the lower-precedence operation and not the other way around. The actual expressions only get evaluated if they need to!
operator && 's order of evaluation is left to right.
= has lower precedence, in fact only ooperator , has lower precedence than =.
So the expresssion will read a = (0 && --b) being 0 evaluated first given the mentioned order of evaluation.
Since 0 evaluates to false, there is no need to evaluate the second part of the expression because false && true is false, given the first part of the expression is false, the expression will always be false.
If you had || operator the second part of the expression would have to be evaluated.
Operator precedence is not all that plays in the game. There is also order of evaluation. And that mandates that a=0 is evaluated first (evaluation order is from left to right), and then right part after the && is not evaluated at all.
That is how C works.
Disclaimer: I don't code like this, I'm just trying to understand how the c language works!!!!
The output is 12.
This expression (a-- == 10 && a-- == 9) evaluates left-to-right, and a is still 10 at a-- == 10 but a is 9 for a-- == 9.
1) Is there a clear rule as to when post-increment evaluate? From this example it seems it evaluates prior to the && but after the ==. Is that because the && logical operator makes a-- == 10 a complete expression, so a is updated after it executes?
2) Also for c/c++, certain operators such as prefix decrement occur right to left so a == --a first decrements a to 9 and then compares 9 == 9. Is there a reason for why c/c++ is designed this way? I know for Java, it's the opposite (it's evaluates left to right).
#include <stdio.h>
int main() {
int a = 10;
if (a-- == 10 && a-- == 9)
printf("1");
a = 10;
if (a == --a)
printf("2");
return 0;
}
The logical && operator contains a sequence point between the evaluation of the first and second operand. Part of this is that any side effect (such as that performed by the -- operator) as part of the left side is complete before the right side is evaluated.
This is detailed in section 6.5.13p4 of the C standard regarding the logical AND operator:
Unlike the bitwise binary & 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
equal to 0, the second operand is not evaluated.
In the case of this expression:
(a-- == 10 && a-- == 9)
The current value of a (10) is first compared for equality against 10. This is true, so the right side is then evaluated, but not before the side effect of decrementing a that was done on the left side. Then, the current value of a (now 9) is compared for equality against 9. This is also true, so the whole expression evaluates to true. Before the next statement is executed, the side effect of decrementing a that was done on the right side is done.
This expression however:
if (a == --a)
Involves reading and writing a in the same expression without a sequence point. This invokes undefined behavior.
This expression (a-- == 10 && a-- == 9) evaluates left-to-right,
Yes, mostly, but only because && is special.
and a is still 10 at a-- == 10
Yes, because a-- yields the old value.
but a is 9 for a-- == 9.
Yes, because the sequence point at && guarantees the update to a's value is complete before the RHS is evaluated.
1) Is there a clear rule as to when post-increment evaluate?
The best answer, I think, is "no". The side effects due to ++ and -- are completed at some point prior to the next sequence point, but beyond that, you can't say. For well-defined expressions, it doesn't matter when the side effects are completed. If an expression is sensitive to when the side effect is completed, that usually means the expression is undefined.
From this example it seems it evaluates prior to the && but after the ==. Is that because the && logical operator makes a-- == 10 a complete expression, so a is updated after it executes?
Basically yes.
2) Also for c/c++, certain operators such as prefix decrement occur right to left
Careful. I'm not sure what you mean, but whatever it is, I'm almost certain it's not true.
so a == --a first decrements a to 9 and then compares 9 == 9.
No, a == --a is undefined. There's no telling what it does.
Is there a reason for why c/c++ is designed this way?
Yes.
I know for Java, it's the opposite (it's evaluates left to right).
Yes, Java is different.
Here are some guidelines to help you understand the evaluation of C expressions:
Learn the rules of operator precedence and associativity. For "simple" expressions, those rules tell you virtually everything you need to know about an expression's evaluation. Given a + b * c, b is multiplied by c and then the product added to a, because of the higher precedence of * over +. Given a + b + c, a is added to b and then the sum added to c, because+ associates from left to right.
With the exception of associativity (as mentioned in point 1), try not to use the words "left to right" or "right to left" evaluation at all. C has nothing like left to right or right to left evaluation. (Obviously Java is different.)
Where it gets tricky is side effects. (When I said "'simple' expressions" in point 1, I basically meant "expressions without side effects".) Side effects include (a) function calls, (b) assignments with =, (c) assignments with +=, -=, etc., and of course (d) increments/decrements with ++ and --. (If it matters when you fetch from a variable, which is typically only the case for variables qualified as volatile, we could add (e) fetches from volatile variables to the list.) In general, you can not tell when side effects happen. Try not to care. As long as you don't care (as long as your program is insensitive to the order in which side effects matter), it doesn't matter. But if your program is sensitive, it's probably undefined. (See more under points 4 and 5 below.)
You must never ever have two side effects in the same expression which attempt to alter the same variable. (Examples: i = i++, a++ + a++.) If you do, the expression is undefined.
With one class of exceptions, you must never ever have a side effect which attempts to alter a variable which is also being used elsewhere in the same expression. (Example: a == --a.) If you do, the expression is undefined. The exception is when the value accessed is being used to compute the value to be stored, as in i = i + 1.
With "logical and" operator (a-- == 10 && a-- == 9) is well-formed expression (without undefined behavior as it is in a++ + a++).
C standard says about "logical and"/"logical or" operators:
guarantees left-to-right evaluation; there is a sequence point after
the evaluation of the first operand.
So that, all side effects of the first subexpression a-- == 10 are complete before the second subexpression a-- == 9 evaluation.
a is 9 before evaluation of second subexpression.
The underlying problem is that the postfix unary operator has both a return value (the starting value of the variable) and a side effect (incrementing the variable). While the value has to be calculated in order, it is explicit in the C++ specs that the sequencing of any side effect relative to the rest of the operators in a statement is undefined, as long as it happens before the full expression completes. This allows compilers (and optimizers) to do what they want, including evaluating them differently on different expressions in the same program.
From the C++20 code spec (C++ 2020 draft N4849 is where I got this from):
Every value computation and side effect associated with a full-expression is
sequenced before every value computation and side effect associated with the
next full-expression to be evaluated.
[6.9.1 9, p.72]
If a side effect on a memory location is unsequenced
relative to either another side effect on the same memory location or
a value computation using the value of any object in the same memory location, and they are not potentially concurrent, the behavior is undefined. [6.9.1 10, p.72]
So, in case you haven't gotten it from other answers:
No, there is no defined order for a postfix operator. However, in your case (a-- == 10 && a-- == 9) has defined behavior because the && enforces that the left side must be evaluated before the right side. It will always return true, and at the end, a==8. Other operators or functions such as (a-- > a--) could get a lot of weird behavior, including a==9 at the end because both prefix operators store the original value of a(10) and decrement that value to 9 and store it back to a.
Not only is the side-effect of setting a=a-1 (in the prefix operator) unsequenced with the rest of this expression, the evaluation of the operands of == is also unsequenced. This expression could:
Evaluate a(10), then evaluate --a(9), then == (false), then set a=9.
Evaluate --a(9), then a(10), then set a=9, then evaluate == (false).
Evaluate --a(9) the set a=9, then evaluate a(9), then evaluate == (true)
Yes, it is very confusing. As a general rule (which I think you already know): Do not set a variable more than once in the same statement, or use it and set it in the same statement. You have no idea what the compiler is going to do with it, especially if this is code that will be published open source, so someone might compile it differently than you did.
Side note:
I've seen so many responses to questions about the undefined behavior of postfix operators that complain that this "undefined behavior" only ever occurs in the toy examples presented in the questions. This really annoys me, because it can and does happen. Here is a real example of how the behavior can change that I had actually happen to me in my legacy code base.
result[ctr]=source[ctr++];
result[ctr++]=(another calculated value without ctr in it);
In Visual Studio, under C++14, this evaluated so that result had every other value of source in even indexes and had the calculated values in the odd indexes. For example, for ctr=0, it would store source[0], copy the stored value to result[0], then increment ctr, then set result[1] to the calculated value, then increment ctr. (Yes, there was a reason for wanting that result.)
We updated to C++20, and this line started breaking. We ended up with a bad array, because it would store source[0], then increment ctr, then copy the stored value to result[1], then set result[1] to the calculated value, then increment ctr. It was only setting the odd indexes in result, first from source then overwriting the source value with the calculated value. All the odd indexes of result stayed zero (our original initialization value).
Ugh.