post and pre-increment operator overloading not behaving like

S

simudream

//hi maybe helpful others can look at this code and
//tell me why the class code won't behave like
//intrinsic types with post and pre increment

//Version: 1.00

#include <iostream>
using std::cout;
using std::endl;
using std::eek:stream;

class IntWrapper
{
friend ostream & operator<<( ostream&, const IntWrapper& );

public:
IntWrapper( int val = 0 ) : i(val) {;}

IntWrapper operator+( const IntWrapper& rhs) const
{
//use RVO
return IntWrapper( this->i + rhs.i );
}

IntWrapper& operator++()
{
++this->i;
return *this;
}

IntWrapper operator++(int)
{
IntWrapper tmp(*this);
this->operator++();
return tmp;
}

private:
int i;

};

ostream &operator<<( ostream &, const IntWrapper & );

int main()
{
//interesting results of the post and pre increment operators

IntWrapper test;
int i = 0;

IntWrapper test1 = test + test++;
int test2 = i + i++;

//test1 should be the same as test2 but they are not

cout << test1 << " <- This should be the same as this one -> " <<
test2 << endl;

cout << i << i++ << test << test++ << endl;

system("pause");
}

ostream &operator<<( ostream &output, const IntWrapper &r )
{
output << r.i;
return output;
}
 
K

Kai-Uwe Bux

//hi maybe helpful others can look at this code and
//tell me why the class code won't behave like
//intrinsic types with post and pre increment [snip]
IntWrapper test1 = test + test++;
int test2 = i + i++;

//test1 should be the same as test2 but they are not

As far as I can see you have unspecified behavior according to clause [5/4]
of the standard. Your assumption that both lines should behave the same is
not waranted by the standard.


Best

Kai-Uwe Bux
 
J

James Kanze

//hi maybe helpful others can look at this code and
//tell me why the class code won't behave like
//intrinsic types with post and pre increment [snip]
IntWrapper test1 = test + test++;
int test2 = i + i++;
//test1 should be the same as test2 but they are not
As far as I can see you have unspecified behavior according to
clause [5/4] of the standard.

Not unspecified. Undefined. At least in the second line. (The
first is unspecified.)
Your assumption that both lines should behave the same is not
waranted by the standard.

The assumption that his program doesn't reformat his hard disk
is not warranted by the standard.
 
K

Kai-Uwe Bux

James said:
//hi maybe helpful others can look at this code and
//tell me why the class code won't behave like
//intrinsic types with post and pre increment [snip]
IntWrapper test1 = test + test++;
int test2 = i + i++;
//test1 should be the same as test2 but they are not
As far as I can see you have unspecified behavior according to
clause [5/4] of the standard.

Not unspecified. Undefined. At least in the second line.

Well, I don't see a variable that has its value modified twice between
sequence points. So it must be the condition

"Furthermore, the prior value shall be accessed only to determine the
value to be stored."

and I have to admit that I never have been able to figure out the meaning of
this sentence.
(The first is unspecified.)

Yup.

[snip]


Best

Kai-Uwe Bux
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
James said:
(e-mail address removed) wrote:
//hi maybe helpful others can look at this code and
//tell me why the class code won't behave like
//intrinsic types with post and pre increment
[snip]
IntWrapper test1 = test + test++;
int test2 = i + i++;
//test1 should be the same as test2 but they are not
As far as I can see you have unspecified behavior according to
clause [5/4] of the standard.
Not unspecified. Undefined. At least in the second line.

Well, I don't see a variable that has its value modified twice between
sequence points. So it must be the condition

"Furthermore, the prior value shall be accessed only to determine the
value to be stored."

and I have to admit that I never have been able to figure out the meaning of
this sentence.

It's probably intended to mean that if an epression modifies a variable,
the expression shall not use the prior value of the variable for
anything else than the computation of the new value, e.g. not displayed
or stored elsewhere. That way the compiler can optimize prematurely to
its heart content without ensuring that the value is consistent during
the evaluation of the expression. But as the incorrect examples in the
standard demonstrate, this part of the standard is bungled, and one must
therefore ignore it & apply common sense instead of the literal wording.

Common sense is to not use side effects in sub-expressions.

Cheers, & hth.

- Alf
 
A

Andre Kostur

(e-mail address removed) wrote in (e-mail address removed):
//hi maybe helpful others can look at this code and
//tell me why the class code won't behave like
//intrinsic types with post and pre increment

//Version: 1.00
[snip code which, IMHO, is correct]
int main()
{
//interesting results of the post and pre increment operators

IntWrapper test;
int i = 0;

IntWrapper test1 = test + test++;

Undefined Behaviour. Reading and modifying a value without an
intervening sequence point.
int test2 = i + i++;

Undefined Behaviour. Reading and modifying a value without an
intervening sequence point. And apparently the Undefined Behaviour is
different between basic types and user defined classes.
 
J

James Kanze

James said:
(e-mail address removed) wrote:
//hi maybe helpful others can look at this code and
//tell me why the class code won't behave like
//intrinsic types with post and pre increment
[snip]
IntWrapper test1 = test + test++;
int test2 = i + i++;
//test1 should be the same as test2 but they are not
As far as I can see you have unspecified behavior according to
clause [5/4] of the standard.
Not unspecified. Undefined. At least in the second line.
Well, I don't see a variable that has its value modified twice
between sequence points. So it must be the condition
"Furthermore, the prior value shall be accessed only to
determine the value to be stored."
and I have to admit that I never have been able to figure out
the meaning of this sentence.

It could be clearer, but what it basically means is that if you
modify the variable, the only legal accesses without an
intervening sequence point are those which are used to determine
the new value. Intuitively, if there is no sequence point, then
cause and effect ordering may still render the code legal: in an
expression like "a = a + 1", there may not be a sequence point
between the two accesses to a, but it's quite clear that the
read must be occur before the write, since we can't know what to
write until the read has occured.

In some ways, the rule is very much like the rule for accessing
in a multithreaded environment: if any access is a modification,
then all accesses must be "protected". Here, the protection may
be provided by an intervening sequence point, or by a strict
value dependency.

IMHO, the word "prior" in the quoted sentence only causes
confusion. In an expression such as "i + i++", does the first i
access the "prior" value, or some later value. In fact, the
standard states elsewhere that there is undefined behavior is
any legal ordering would result in undefined behavior. Since
the ordering which evaluates the left hand side of the +
operator first does access the "prior" value, the expression has
undefined behavior. But as there are no cases without an
intervening sequence point which don't allow such reordering,
the "prior" is really not needed.
 
J

James Kanze

* Kai-Uwe Bux:
James said:
(e-mail address removed) wrote:
//hi maybe helpful others can look at this code and
//tell me why the class code won't behave like
//intrinsic types with post and pre increment
[snip]
IntWrapper test1 = test + test++;
int test2 = i + i++;
//test1 should be the same as test2 but they are not
As far as I can see you have unspecified behavior according to
clause [5/4] of the standard.
Not unspecified. Undefined. At least in the second line.
Well, I don't see a variable that has its value modified
twice between sequence points. So it must be the condition
"Furthermore, the prior value shall be accessed only to
determine the value to be stored."
and I have to admit that I never have been able to figure
out the meaning of this sentence.
It's probably intended to mean that if an epression modifies a
variable, the expression shall not use the prior value of the
variable for anything else than the computation of the new
value, e.g. not displayed or stored elsewhere.

The prior value may not be accessed, period, other than to
determine the new value. You don't have to display or store it
elsewhere---you just have to use it in an expression. (Of
course, I'm not sure that means anything in practice. But it's
what the standard says.)
That way the compiler can optimize prematurely to its heart
content without ensuring that the value is consistent during
the evaluation of the expression. But as the incorrect
examples in the standard demonstrate,

Which "incorrect examples"? The only incorrection I see in the
examples is the use of the word "unspecified" rather than
"undefined" in the comments. The preceding (normative) text
makes it quite clear, however, that the behavior in the examples
is undefined. (There are other cases where it isn't so clear,
of course.)
this part of the standard is bungled, and one must therefore
ignore it & apply common sense instead of the literal wording.

That's your opinion, and it isn't shared by the experts (unless
you mean something different by "bungled" than I understand).

Generally speaking, it is recognized that the model used to
express the constraints here is not ideal. The C committee
discussed the issues when updating the C standard (resulting in
C99). The consensus among the experts at the time was that
while not the best, the current wording was sufficient for what
was wanted, and that a rewrite wasn't necessary. (The rules
here are taken verbatim from the C standard.)

Of course, the rules don't address threading issues. Since the
C++ standard is addressing threading, this section does require
rework, and is the subject of a complete rewrite. In
collaboration with the C committee---the intent is that whatever
we rewrite will be acceptable to them, and integrated into the
next version of the C standard as well.
Common sense is to not use side effects in sub-expressions.

Ideally: each statement does one thing, and one thing only. A
flow control statement doesn't modify program state, and a
statement which modifies an object doesn't modify any other
program state, nor affect flow control.

Practically, there are exceptions. But they should be very
rare (and almost always concern modifying state in a flow
control statement, and not modifying two different objects in a
single statement).

Hopefully, we've gotten past the days when people thought that
things like:
while ( *p ++ = *q ++ ) ;
was acceptable in production code.
 
A

Alf P. Steinbach

* James Kanze:
* Kai-Uwe Bux:
James Kanze wrote:
(e-mail address removed) wrote:
//hi maybe helpful others can look at this code and
//tell me why the class code won't behave like
//intrinsic types with post and pre increment
[snip]
IntWrapper test1 = test + test++;
int test2 = i + i++;
//test1 should be the same as test2 but they are not
As far as I can see you have unspecified behavior according to
clause [5/4] of the standard.
Not unspecified. Undefined. At least in the second line.
Well, I don't see a variable that has its value modified
twice between sequence points. So it must be the condition
"Furthermore, the prior value shall be accessed only to
determine the value to be stored."
and I have to admit that I never have been able to figure
out the meaning of this sentence.
It's probably intended to mean that if an epression modifies a
variable, the expression shall not use the prior value of the
variable for anything else than the computation of the new
value, e.g. not displayed or stored elsewhere.

The prior value may not be accessed, period, other than to
determine the new value. You don't have to display or store it
elsewhere---you just have to use it in an expression. (Of
course, I'm not sure that means anything in practice. But it's
what the standard says.)

Exactly how do you think that's different from what I wrote?

Which "incorrect examples"? The only incorrection I see in the
examples is the use of the word "unspecified" rather than
"undefined" in the comments. The preceding (normative) text
makes it quite clear, however, that the behavior in the examples
is undefined. (There are other cases where it isn't so clear,
of course.)

Again, how is that different from what I wrote?

That's your opinion, and it isn't shared by the experts (unless
you mean something different by "bungled" than I understand).

It's being corrected in C++0x.

And I remind you that you have participated in extended discussions in
e.g. comp.std.c++ about what the wording means, where you have taken on
minority positions.

Your reference to unspecified "experts" is therefore (a) evidently
incorrect, and (b) fallacious anyway.

Generally speaking, it is recognized that the model used to
express the constraints here is not ideal. The C committee
discussed the issues when updating the C standard (resulting in
C99). The consensus among the experts at the time was that
while not the best, the current wording was sufficient for what
was wanted, and that a rewrite wasn't necessary. (The rules
here are taken verbatim from the C standard.)

Of course, the rules don't address threading issues. Since the
C++ standard is addressing threading, this section does require
rework, and is the subject of a complete rewrite. In
collaboration with the C committee---the intent is that whatever
we rewrite will be acceptable to them, and integrated into the
next version of the C standard as well.

You may of course, by association, include yourself in the panel of
people rewriting this stuff, if you're participating in the committee
(do you, still?), and you may attribute the changes to anything vaguely
associated. Let the associations run wild. :)

Ideally: each statement does one thing, and one thing only. A
flow control statement doesn't modify program state, and a
statement which modifies an object doesn't modify any other
program state, nor affect flow control.

Practically, there are exceptions. But they should be very
rare (and almost always concern modifying state in a flow
control statement, and not modifying two different objects in a
single statement).

Hopefully, we've gotten past the days when people thought that
things like:
while ( *p ++ = *q ++ ) ;
was acceptable in production code.

It's an idiom, presented in the first version of "The C Programming
Language" by Kernighan & Ritchie.

Cheers, & hth.,

- Alf
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top