Why is this expression illegal?

C

Chris Yates

Why is this illegal:

--(i++) // Error: -- needs an l-value

and this is not:

(--i)++

??

Why is i++ not an l-value but --i is? Pre incrementor adds 1 to the l-value
and returns the l-value, so that explains that. However, I thought i++
would return the l-value and the increment it after the expression?

-
Chris
 
V

Victor Bazarov

Chris said:
Why is this illegal:

--(i++) // Error: -- needs an l-value

and this is not:

(--i)++

??

Why is i++ not an l-value but --i is? Pre incrementor adds 1 to the l-value
and returns the l-value, so that explains that. However, I thought i++
would return the l-value and the increment it after the expression?

Nope. Post-increment returns an r-value. When it increments the actual
object is unspecified.

V
 
D

David Lindauer

Chris said:
Why is this illegal:

--(i++) // Error: -- needs an l-value

and this is not:

(--i)++

??

Why is i++ not an l-value but --i is? Pre incrementor adds 1 to the l-value
and returns the l-value, so that explains that. However, I thought i++
would return the l-value and the increment it after the expression?

if i is say an integer, --i (or ++i) does something before accessing the
variable... whereas i++(or i--) does something after accessing the variable. In
the former case the variable changes before you get its value so everything is
kosher, but in the latter case the variable may change value between the time it
is fetched and the time you use it. (I don't know if it HAS to change
immediately or it can wait till the next sequence point, but there are common
cases where compilers do try to adhere to changing it immediately to make users
happy).

David
 
B

Bob Hairgrove

Why is this illegal:

--(i++) // Error: -- needs an l-value

and this is not:

(--i)++

??

Why is i++ not an l-value but --i is? Pre incrementor adds 1 to the l-value
and returns the l-value, so that explains that. However, I thought i++
would return the l-value and the increment it after the expression?

I am not sure, but I believe both expressions lead to undefined
behaviour, RE: the C++ standard, due to the lack of a sequence point
between increment and decrement.

There are some threads on comp.std.c++ about this. Please try to look
at these, as well as the standard itself, for more of an in-depth
explanation. Unfortunately, I don't have the subject lines at hand...
 
A

Andrey Tarasevich

Chris said:
Why is this illegal:

--(i++) // Error: -- needs an l-value

and this is not:

(--i)++

??

Both are illegal, but for different reasons. The first one is
ill-formed, the second one causes undefined behavior.
Why is i++ not an l-value but --i is? Pre incrementor adds 1 to the l-value
and returns the l-value, so that explains that. However, I thought i++
would return the l-value and the increment it after the expression?

The C++ language doesn't specify what happens "before" and what happens
"after" in this case. The argument of the post-increment ('i') might get
incremented immediately, it might get incremented "after the
expression", it might get incremented "before the expression" - no one
knows and no one cares, since in any case the result must be the
original value of 'i'. Hence the potential need to take it out of 'i'
and store it "on the side", as an rvalue.
 
R

Rolf Magnus

Chris said:
Why is this illegal:

--(i++) // Error: -- needs an l-value

and this is not:

(--i)++

??

Why is i++ not an l-value but --i is? Pre incrementor adds 1 to the
l-value
and returns the l-value, so that explains that. However, I thought i++
would return the l-value and the increment it after the expression?

Think about it. How would the operator return something, and _after_ that
change the value? After it returned, it cannot do anything anymore.
 
A

Andre Dajd

Victor Bazarov said:
Nope. Post-increment returns an r-value. When it increments the actual
object is unspecified.

Post increment, actually, returns a copy of the original object, which
is created before the original object is modified. Therefore
(offtopic) the postfix operator is, actually, slower.

Standard 5.2.6.

d
 
R

Richard Herring

Andrey Tarasevich said:
Both are illegal, but for different reasons. The first one is
ill-formed, the second one causes undefined behavior.


The C++ language doesn't specify what happens "before" and what happens
"after" in this case.

More to the point, nothing in the above says that i has to be of a
built-in type. If the operator is user-defined, there _is_ no "before"
and "after". Everything the operator does must take place _during_ the
call to it. The only way you can get the right semantics for
post-increment within a single function call is to take a copy, then
increment i, then return the copy by value, hence an rvalue.
 
A

Andrey Tarasevich

Andre said:
Post increment, actually, returns a copy of the original object, which
is created before the original object is modified.

Incorrect. The notion of "returning a copy" (or simply "returning") the
way you use it is not applicable to built-in increment operators.
Therefore (offtopic) the postfix operator is, actually, slower.

True for user-defined operators. Completely untrue for built-in ones.
And we are talking about the latter here.
Standard 5.2.6.

I don't see anything you said in 5.2.6.
 
A

Andre Dajd

Andrey Tarasevich said:
Incorrect. The notion of "returning a copy" (or simply "returning") the
way you use it is not applicable to built-in increment operators.

See first [Note] in the citation below and note world "copy" there.
Fine, I withdraw word "return", as using it in the context requires
abstract thinking beyond repeating C++ Standard's deficiencies.
Mathematically (=abstractly) every operation (mapping) "returns"
something (has image).
True for user-defined operators. Completely untrue for built-in ones.
And we are talking about the latter here.

Original question did not imply that, neither did any post in this
subthread. Mind your perceptions.
I don't see anything you said in 5.2.6.

[expr.post.incr] 5.2.6 Increment and decrement

The value obtained by applying a postfix ++ is the value that the
operand had before applying the operator.
[Note: the value obtained is a copy of the original value ] The
operand shall be a modifiable lvalue. The
type of the operand shall be an arithmetic type or a pointer to a
complete object type. After the result is
noted, the value of the object is modified by adding 1 to it, unless
the object is of type bool, in which case
it is set to true. [Note: this use is deprecated, see annex D. ] The
result is an rvalue. The type of the
result is the cvunqualified
version of the type of the operand. See also 5.7 and 5.17.
 
A

Andrey Tarasevich

Andre said:
Incorrect. The notion of "returning a copy" (or simply "returning") the
way you use it is not applicable to built-in increment operators.

See first [Note] in the citation below and note world "copy" there.
Fine, I withdraw word "return", as using it in the context requires
abstract thinking beyond repeating C++ Standard's deficiencies.
Mathematically (=abstractly) every operation (mapping) "returns"
something (has image).

The problem with your original statement is that it imposes a ordering
on the sequence of operations as in 1) create a copy 2) increment the
value 3) return the copy. I was objecting the use of the term "return"
as an atomic action in some sequence of operations, because none of this
is applicable to built-in operators.
Original question did not imply that, neither did any post in this
subthread. Mind your perceptions.

Did not imply? Original question did not _state_ that explicitly, but it
did _imply_ it very clearly. The original question stated the increment
operators in question "add 1" and did not mention anything about those
operators being user-defined. That's "implying" in my book.

Moreover, you yourself implied exactly the same thing by providing a
reference to 5.2.6, which specifically applies to built-in operators,
and built-in operators only.
I don't see anything you said in 5.2.6.

[expr.post.incr] 5.2.6 Increment and decrement

The value obtained by applying a postfix ++ is the value that the
operand had before applying the operator.
[Note: the value obtained is a copy of the original value ]

The purpose of this note is simply to indicate that the result is not a
lvalue. It doesn't in any way indicate that there's is some kind of a
preliminary step in the evaluation of post-increment, when this copy is
made so as to save the old value from being destroyed. There's no need
to do that.

You said that post-increment returns "copy of the original object, which
is created before the original object is modified".

Firstly, this statement imposes a strict ordering on the process of
evaluation of post-increment. This is always incorrect in case of
built-in operators. There are no temporal orderings in the C++ program
besides those imposed by sequence points.

Secondly, you state that the copy in question is created by copying the
object in its original state. There's noting in the standard that says
that the copy is created in this fashion. In case of built-in
post-increment the copy of the old value can be obtained by subtracting
1 from the new value. I.e. the copy can still be created even _after_
the side effect of post-increment took place.
 
A

andre dajd

Did not imply? Original question did not _state_ that explicitly, but it
did _imply_ it very clearly. The original question stated the increment
operators in question "add 1" and did not mention anything about those
operators being user-defined. That's "implying" in my book.

He did not mention that he meant built-in either, so we apparently
interpreted the implication differently. I was inclined to interpret it the
way I did, because, I think, this is much more generic case and that
therefore it would be valuable to mention the efficiency issue. Guess how
many people still use postix ++ in "for" loops, using STL iterators and not
built-in types?
The value obtained by applying a postfix ++ is the value that the
operand had before applying the operator.
[Note: the value obtained is a copy of the original value ]

The purpose of this note is simply to indicate that the result is not a
lvalue. It doesn't in any way indicate that there's is some kind of a
preliminary step in the evaluation of post-increment, when this copy is
made so as to save the old value from being destroyed. There's no need
to do that.

You said that post-increment returns "copy of the original object, which
is created before the original object is modified".

Firstly, this statement imposes a strict ordering on the process of
evaluation of post-increment. This is always incorrect in case of
built-in operators. There are no temporal orderings in the C++ program
besides those imposed by sequence points.

Secondly, you state that the copy in question is created by copying the
object in its original state. There's noting in the standard that says
that the copy is created in this fashion. In case of built-in
post-increment the copy of the old value can be obtained by subtracting
1 from the new value. I.e. the copy can still be created even _after_
the side effect of post-increment took place.

C'mon, read two more sentences after your cut of my citation of the
standard. I reproduce it again.

****
The value obtained by applying a postfix ++ is the value that the
operand had before applying the operator.
[Note: the value obtained is a copy of the original value ] The
operand shall be a modifiable lvalue. The
type of the operand shall be an arithmetic type or a pointer to a
complete object type. After the result is
noted, the value of the object is modified by adding 1 to it, unless
the object is of type bool, in which case
it is set to true. [Note: this use is deprecated, see annex D. ] The
result is an rvalue
****

What draws my attention is the line "After the result is
noted, the value of the object is modified by adding 1".

In my book, this is temporal ordering. "The result is noted" = recorded,
stored, whatever, and then the object (original) is modified. This ordering
has nothing to do with sequence points though, as all steps must be
completed before the next sequence point. But there is, apparently,
ordering. And I meant only that.

At the same time, I have to admit, that compilers may not follow this line
exactly. Out of curiosity,
I played with source

int f(int& i, int& j)
{
++i;
j++;
return j; // to pick up the side effect
}

and some of it's variations with VC6 and looked at the assembly produced the
in speed optimization release mode. The number of operations "attributable"
to each line was not necessarily in favour in ++i, so I tend to agree that
one should not generalize the case of user-defined operator's efficiency to
that of the built-in one.

Rgds

ad
 
A

Andrey Tarasevich

andre said:
He did not mention that he meant built-in either, so we apparently
interpreted the implication differently. I was inclined to interpret it the
way I did, because, I think, this is much more generic case and that
therefore it would be valuable to mention the efficiency issue.

I disagree. Assuming that there's _no_ strict temporal ordering in the
sequence of steps performed by increment operator is more generic approach.
Guess how
many people still use postix ++ in "for" loops, using STL iterators and not
built-in types?

That's a separate issue.
The value obtained by applying a postfix ++ is the value that the
operand had before applying the operator.
[Note: the value obtained is a copy of the original value ]

The purpose of this note is simply to indicate that the result is not a
lvalue. It doesn't in any way indicate that there's is some kind of a
preliminary step in the evaluation of post-increment, when this copy is
made so as to save the old value from being destroyed. There's no need
to do that.

You said that post-increment returns "copy of the original object, which
is created before the original object is modified".

Firstly, this statement imposes a strict ordering on the process of
evaluation of post-increment. This is always incorrect in case of
built-in operators. There are no temporal orderings in the C++ program
besides those imposed by sequence points.

Secondly, you state that the copy in question is created by copying the
object in its original state. There's noting in the standard that says
that the copy is created in this fashion. In case of built-in
post-increment the copy of the old value can be obtained by subtracting
1 from the new value. I.e. the copy can still be created even _after_
the side effect of post-increment took place.

C'mon, read two more sentences after your cut of my citation of the
standard. I reproduce it again.

****
The value obtained by applying a postfix ++ is the value that the
operand had before applying the operator.
[Note: the value obtained is a copy of the original value ] The
operand shall be a modifiable lvalue. The
type of the operand shall be an arithmetic type or a pointer to a
complete object type. After the result is
noted, the value of the object is modified by adding 1 to it, unless
the object is of type bool, in which case
it is set to true. [Note: this use is deprecated, see annex D. ] The
result is an rvalue
****

What draws my attention is the line "After the result is
noted, the value of the object is modified by adding 1".

In my book, this is temporal ordering. "The result is noted" = recorded,
stored, whatever, and then the object (original) is modified. This ordering
has nothing to do with sequence points though, as all steps must be
completed before the next sequence point. But there is, apparently,
ordering. And I meant only that.

OK. But I strongly believe that this is noting more than a bad wording.
There are no guaranteed temporal orderings in C++ language other than
those imposed by sequence points - that was the idea, I believe.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,183
Messages
2,570,967
Members
47,524
Latest member
ecomwebdesign

Latest Threads

Top