Prefix/Postfix Increment Operators

POSTFIX and PREFIX increment/decrement precedence in JavaScript

Operator precedence is not the same thing as evaluation order.

Operator precedence tells you that

f() + g() * h()

is parsed as

f() + (g() * h())

(because * has higher precedence than +), but not which function is called first. That is controlled by evaluation order, which in JavaScript is always left-to-right.

Parentheses only override precedence (i.e. they affect how subexpressions are grouped), not order of evaluation:

(f() + g()) * h()

performs addition before multiplication, but in all cases f is called first and h last.

In your example

--a + a++

the relative precedence of prefix -- and postfix ++ doesn't matter because they're not attached to the same operand. Infix + has much lower precedence, so this expression parses as

(--a) + (a++)

As always, JS expressions are evaluated from left to right, so --a is done first.

If you had written

--a++

that would have been parsed as

--(a++)

(and not (--a)++) because postfix ++ has higher precedence, but the difference doesn't matter here because either version is an error: You can't increment/decrement the result of another increment/decrement operation.

However, in the operator precedence table you can see that all prefix operators have the same precedence, so we can show an alternative:

// ! has the same precedence as prefix ++
!a++

is valid code because it parses as !(a++) due to postfix ++ having higher precedence than !. If it didn't, it would be interpreted as (!a)++, which is an error.

Prefix/Postfix increment operators

This is a correct implementation. It is typical that a postfix operator will be worse on performance because you have to create another copy before doing the increment (and this is why I've gotten in the habit of always using prefix unless I need something else).

With return-by-reference, you're returning an l-value reference to the current object. The compiler would typically implement this by returning the address of the current object. This means that returning the object is as simple as returning a number.

However, with return-by-value, a copy must be done. This means there's more information to copy over during the return (instead of just an address) as well as a copy constructor to call. This is where your performance hit comes in.

The efficiency of your implementation looks on-par with typical implementations.

EDIT:
With regards to your addendum, no, they are not aliases. You have created two separate objects. When you return by value (and when you created a new object from within the postfix increment operator) this new object is placed in a distinct memory location.

However, in the following code, a and b are aliases:

 int a = 0;
int& b = ++a;

b is an address which references a.

Difference between prefix and postfix ++ operators in Java

The relevant parts of the specification are:

15.15.1. Prefix Increment Operator ++

[…]

At run time, if evaluation of the operand expression completes abruptly, then the prefix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. […]

The value of the prefix increment expression is the value of the variable after the new value is stored.

 

15.14.2. Postfix Increment Operator ++

[…]

At run time, if evaluation of the operand expression completes abruptly, then the postfix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. […]

The value of the postfix increment expression is the value of the variable before the new value is stored.

So, the only difference is the result value when used in an expression context. When using it in the context of a for loop’s update clause, there relevant part is:

14.14.1.2. Iteration of for Statement

[…]

→ First, if the ForUpdate part is present, the expressions are evaluated in sequence from left to right; their values, if any, are discarded. If evaluation of any expression completes abruptly for some reason, the for statement completes abruptly for the same reason; any ForUpdate statement expressions to the right of the one that completed abruptly are not evaluated.

Taking it literally there would be a difference in the code as these expressions produce different results which are then discarded. However, this difference lies in non-observable behavior and as a consequence, the code is usually compiled to not produce the value in the first place and hence does not differ.

So the answer is, the code may have differences, e.g. when compiled naively, however the observable behavior is guaranteed to be the same.

javascript Postfix and Prefix Increment in expression

It is evaluated left to right:

++x           : x is now 2
++x + : 2 +
++x + x : 2 + 2
++x + x++ : 2 + 2 and x is now 3
++x + x++ * : 2 + 2 *
++x + x++ * x : 2 + 2 * 3

Usage of prefix or postfix increment (or decrement)

You are right about using prefix or postfix increment operator doesn't matter in this example, but C++ Primer also says that only use postfix version when really necessary as a best practice, I'm quoting the book from §4.5. Increment and Decrement Operators

Advice: Use Postfix Operators only When Necessary

Readers from a C background might be surprised that we use the prefix increment in the programs we've written. The reason is simple: The prefix version avoids unnecessary work. It increments the value and returns the incremented version.The postfix operator must store the original value so that it can return the unincremented value as its result. If we don’t need the unincremented value, there’s no need for the extra work done by the postfix operator.

For ints and pointers, the compiler can optimize away this extra work. For more complicated iterator types, this extra work potentially might be more costly. By habitually using the prefix versions, we do not have to worry about whether the performance difference matters. Moreover—and perhaps more importantly—we can express the intent of our programs more directly.

Difference between prefix and postfix increment operators overloading implementations for iterators in C++

Actually, both postfix versions are wrong. The postfix iterator must return a copy, not a reference.

The point is that post-increment changes the incremented object, but returns a version of it before the increment. The pre-increment changes the object and returns the incremented version. Their logic must differ accordingly.

This distinction is made because post- and pre-increment have the same semantics on primitive types. Example:

int i = 0;
std::cout << i++ << std::endl;

yields the output 0. Your class with overloaded iterators should imitate that behaviour for consistency. If you had your own integer class with one of the iterator implementations you've shown, the result would be 1, and thereby surprising.

The correct implementation of a postfix increment is in 99% of the cases:

iterator operator++(int)
{
iterator old = *this;
++(*this);
return old;
}


Related Topics



Leave a reply



Submit