Sequence point and evaluation order

24 views
Skip to first unread message

Lee

unread,
Feb 15, 2003, 4:58:02 AM2/15/03
to
Dear Mr/Ms. Gurus,

A beginner's question here:

<Program 1>

class T
{
public:
int m_b;
T(int i=0): m_b(i) {}
T& operator ++(void) { ++m_b; return *this;}
T operator +(cosnt T& rhs) { m_b+=rhs.m_b; return *this;}
void foo(void) {cout<<m_b<<endl;}
};

int main()
{
T t1(2);
T t2=(++t1)+(++t1); //undefined?
t2.foo();
return 0;
}

I believe the line t2=(++t1)+(++t1) is undefined as there is no
sequence point? Correct?


Consider the following <program 2>

int& foo(int& i)
{
return ++i;
}

int main()
{
int i=2;
int j=foo(i)+foo(i); //undefined?
cout<<j<<endl;
return 0;
}

I guess j=foo(i)+foo(i) is also undefined, isn't it? However I was
told this program is well-defined. Anybody can tell me why?

It is not a surprise to see both programs give the same result output.

Thank you very much.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Andrea Griffini

unread,
Feb 15, 2003, 2:47:47 PM2/15/03
to
On 15 Feb 2003 04:58:02 -0500, grace...@yahoo.com (Lee) wrote:

>Dear Mr/Ms. Gurus,

No guru here, but I hope this is not going to be a problem

>A beginner's question here:
>
><Program 1>
>
>class T
>{
>public:
> int m_b;
> T(int i=0): m_b(i) {}
> T& operator ++(void) { ++m_b; return *this;}
> T operator +(cosnt T& rhs) { m_b+=rhs.m_b; return *this;}
> void foo(void) {cout<<m_b<<endl;}
>};
>
>int main()
>{
> T t1(2);
> T t2=(++t1)+(++t1); //undefined?
> t2.foo();
> return 0;
>}
>
>I believe the line t2=(++t1)+(++t1) is undefined as there is no
>sequence point? Correct?

Wrong (IMO). There is a sequence point every time there
is a function called. Calling operator++ on a class
count as a function call (and doesn't matter if that
function/method is inline or not; still the sequence
point is present). Your definition of operator+ is
surely kind of strange (a+b modifies a, and this to
me seems quite unnatural) but I see no undefined
behaviour in this program.
I can see two different possible execution orders of
(++t1)+(++t1): the left side of the "+" operator may
be evaluated before or after the right side.
However the last call will always compute the sum
calling T::operator+ passing to t1 a reference to t1
as rhs... when that happens both the increments will
have been performed already so the instance will have
m_b==4. The output of this program will be 8 no matter
if it's the right side or the left side of operator+
that is evaluated first.

>Consider the following <program 2>
>
>int& foo(int& i)
>{
> return ++i;
>}
>
>int main()
>{
> int i=2;
> int j=foo(i)+foo(i); //undefined?
> cout<<j<<endl;
> return 0;
>}
>
>I guess j=foo(i)+foo(i) is also undefined, isn't it? However I was
>told this program is well-defined. Anybody can tell me why?

If you mean that it's undefined behaviour then my
opinion is that you're wrong. Like before there is
a sequence point for every function call, and there
is no problem if exection of both modifies the same
variable. Differently from before however now the
sum can in my opinion give different results because
of the unspecified order of exection of the conversion
between int& and int in the addition operation.
I think that legal results could be both 7 and 8,
because the former happens if the int&->int conversion
is done just after the calling of foo, and the latter
happens if the int&->int conversions are performed
just before the computation of the sum.

>It is not a surprise to see both programs give the same result output.

I think the result of the second program is
implementation dependent, while the result of
the first program is well defined being 8.
On g++ and vc++ I get 8 in both programs, with
BCC55 I get 8 from the first program and 7 from
the second; I however don't think this qualifies
as another bug in BCC55.

HTH
Andrea

Francis Glassborow

unread,
Feb 15, 2003, 2:51:40 PM2/15/03
to
In message <e80c8ec3.0302...@posting.google.com>, Lee
<grace...@yahoo.com> writes

>Dear Mr/Ms. Gurus,
>
>A beginner's question here:
>
><Program 1>
>
>class T
>{
>public:
> int m_b;
> T(int i=0): m_b(i) {}
> T& operator ++(void) { ++m_b; return *this;}
> T operator +(cosnt T& rhs) { m_b+=rhs.m_b; return *this;}
> void foo(void) {cout<<m_b<<endl;}
>};
>
>int main()
>{
> T t1(2);
> T t2=(++t1)+(++t1); //undefined?

No because that is equivalent to creating t2 with:
T(t1.operator++() + t1.operator++())

those operator++() are function calls and function calls have a sequence
point on entry and another on exit. That is a major difference between
operators for built-in types and for user defined types.


> t2.foo();
> return 0;
>}
>
>I believe the line t2=(++t1)+(++t1) is undefined as there is no
>sequence point? Correct?

There are actually at least six sequence points hidden away in those
implicit function calls.

>
>
>Consider the following <program 2>
>
>int& foo(int& i)
>{
> return ++i;
>}
>
>int main()
>{
> int i=2;
> int j=foo(i)+foo(i); //undefined?

No for the same reason.


> cout<<j<<endl;
> return 0;
>}
>
>I guess j=foo(i)+foo(i) is also undefined, isn't it?

No because of the sequence points at entry to and exit from a function.

> However I was
>told this program is well-defined. Anybody can tell me why?
>
>It is not a surprise to see both programs give the same result output.

Even if the behaviour were undefined you could not discover that by
testing code because undefined behaviour can always do what is naively
expected (and often does until someone's life or future is at stake)

--
ACCU Spring Conference 2003 April 2-5
The Conference you cannot afford to miss
Check the details: http://www.accuconference.co.uk/
Francis Glassborow ACCU

LLeweLLyn

unread,
Feb 16, 2003, 6:33:35 AM2/16/03
to
grace...@yahoo.com (Lee) writes:

> Dear Mr/Ms. Gurus,
>
> A beginner's question here:
>
> <Program 1>
>
> class T
> {
> public:
> int m_b;
> T(int i=0): m_b(i) {}
> T& operator ++(void) { ++m_b; return *this;}
> T operator +(cosnt T& rhs) { m_b+=rhs.m_b; return *this;}

An operator+ that modifies its left-hand operand? Freaky. No-one will
expect or understand that. (I'd say more, but your question isn't
about this.)

> void foo(void) {cout<<m_b<<endl;}
> };
>
> int main()
> {
> T t1(2);
> T t2=(++t1)+(++t1); //undefined?
> t2.foo();
> return 0;
> }
>
> I believe the line t2=(++t1)+(++t1) is undefined as there is no
> sequence point? Correct?

The two (++t1) expressions may executed in either order, but may not
be interleaved. There *is*, however, a sequence point before the
call of operator+, and after the two (++t1) expressions have
completed. That much I am sure of.

I'm less certain of what follows, but I believe the behavior is
unspecified, but not undefined; the two (++t1) expressions may be
executed in either order, but the implementation is not allowed
garble them completely. Further, I believe both orderings have
identical results.

Note that while a userdefined operator++ is a function, and therefor
obeys sequence points, non-interleaving, etc, as a function does,
a builtin operator++ is *not* a function, and:

int t1(2);
int t2=(++t1)+(++t1);

is undefined.

> Consider the following <program 2>
>
> int& foo(int& i)
> {
> return ++i;
> }
>
> int main()
> {
> int i=2;
> int j=foo(i)+foo(i); //undefined?
> cout<<j<<endl;
> return 0;
> }
>
> I guess j=foo(i)+foo(i) is also undefined, isn't it?

[snip]

It is equivalent to the first program.

John Potter

unread,
Feb 16, 2003, 6:35:00 AM2/16/03
to
On 15 Feb 2003 04:58:02 -0500, grace...@yahoo.com (Lee) wrote:

> class T
> {
> public:
> int m_b;
> T(int i=0): m_b(i) {}
> T& operator ++(void) { ++m_b; return *this;}
> T operator +(cosnt T& rhs) { m_b+=rhs.m_b; return *this;}

Considering what it does, it should be operator+=.

> void foo(void) {cout<<m_b<<endl;}
> };

> int main()
> {
> T t1(2);
> T t2=(++t1)+(++t1); //undefined?

This is:

T t2 = t1.operator++().operator+(t1.operator++());

which is effectively

operator+(&operator++(&t1), operator++(&t1));

There are three function calls each of which introduces at
least two sequence points. There is nothing undefined here.
It is unspecified which of the two operator++ functions will
be called first, but they are sequential and the end result
will be that t1.m_b will have the value 4 after they have both
completed. Operator+ will then be called which does m_b += m_b
giving a final value of 8.

> t2.foo();
> return 0;
> }

> I believe the line t2=(++t1)+(++t1) is undefined as there is no
> sequence point? Correct?

No, there are at least 6 sequence points in that expression.

> Consider the following <program 2>

> int& foo(int& i)
> {
> return ++i;
> }

> int main()
> {
> int i=2;
> int j=foo(i)+foo(i); //undefined?
> cout<<j<<endl;
> return 0;
> }

> I guess j=foo(i)+foo(i) is also undefined, isn't it? However I was
> told this program is well-defined. Anybody can tell me why?

As above, the two function calls introduce sequence points. I will be
incremented in each call sequentially resulting in 4. Now we have the
builtin operator+ which is not a function and uses values not references.
Since foo returns a reference, the value for the first call could be
obtained as 3 prior to the second call or as 4 after the second call. The
value from the second call will be 4. We now have the possibility of the
unspecified results being either 7 or 8. Still nothing undefined. Change
from + to - and we have the possibilities of -1, 0, 1 depending upon the
order of the calls and when the first return is dereferenced. Again this
is unspecified not undefined. The result is in a fixed set of
possibilities and your hard drive is safe from formatting unless you do
something which depends upon an expected result.

John

Rob

unread,
Feb 16, 2003, 6:45:46 PM2/16/03
to
"Lee" <grace...@yahoo.com> wrote in message
news:e80c8ec3.0302...@posting.google.com...

> Dear Mr/Ms. Gurus,
>
> A beginner's question here:
>
> <Program 1>
>
> class T
> {
> public:
> int m_b;
> T(int i=0): m_b(i) {}
> T& operator ++(void) { ++m_b; return *this;}
> T operator +(cosnt T& rhs) { m_b+=rhs.m_b; return *this;}
> void foo(void) {cout<<m_b<<endl;}
> };
>
> int main()
> {
> T t1(2);
> T t2=(++t1)+(++t1); file://undefined?

> t2.foo();
> return 0;
> }
>
> I believe the line t2=(++t1)+(++t1) is undefined as there is no
> sequence point? Correct?

I would interpret this as unspecified behaviour, rather than undefined
behaviour. The difference between these is that undefined behaviour
means there is no constraint imposed on the compiler, but unspecified
behaviour means that there is a finite set of distinct results, and a
real-world compiler must emit one of those.

The standard guarantees that t1 will be increment twice, and the
results then added. It does not specify whether the left or right
version of "(++t1)" will be executed first.

The line T t2 = (++t1) + (++t1);

may be expanded to something like

T temp1 = (++t1); // temp1 will contain 3
T temp2 = (++t1); // temp2 will contain 4
T T2(temp1.operator+(temp2)); // this will add 4 to 3

or to

T temp1 = (++t1); // temp1 will contain 3
T temp2 = (++t1); // temp2 will contain 4
T T2(temp2.operator+(temp1)); // this will add 3 to 4

the differences between these two ways of ordering things can
be detected by the operator+ function. In this case, in practice,
the end result will be the same: t1 will be incremented twice,
and T2 will contain a value of 7.

>
>
> Consider the following <program 2>
>
> int& foo(int& i)
> {
> return ++i;
> }
>
> int main()
> {
> int i=2;

> int j=foo(i)+foo(i); file://undefined?


> cout<<j<<endl;
> return 0;
> }
>
> I guess j=foo(i)+foo(i) is also undefined, isn't it? However I was
> told this program is well-defined. Anybody can tell me why?

Again this is unspecified behaviour. We are guaranteed that foo()
will be called twice, and the results added then added.

>
> It is not a surprise to see both programs give the same result output.
>

I agree.

You might wish to try the following on for size.

int& foo(int& i)
{
return ++i;
}

int& foo(int &i)
{
i *= 5;
return i;
}

int main()
{
int i=2;
int j=foo(i)+goo(i);


cout<<j<<endl;
return 0;
}

Depending on your compiler, the value printed will be
either 18 (=3+15) or 21 (=10+11). The values of i,
if you print them, will be 15 or 11 respectively.

Reply all
Reply to author
Forward
0 new messages