Evaluation sequence and precedence

F

feeblez

Consider the following,

#include <iostream>
using namespace std;
class Foo
{
public:
int i;
Foo() : i(0)
{ cout << "ctor" << endl; }
~Foo()
{ cout << "dtor" << endl; }
Foo(const Foo& rhs) : i(rhs.i)
{ cout << "copy ctor" << endl; }
Foo operator++(int)
{ cout << "postincrement" << endl; Foo temp = *this; ++i;
return temp; }
Foo& operator++()
{ cout << "preincrement" << endl; ++i; return *this; }
Foo& operator=(const Foo& rhs)
{ cout << "assignment" << endl; i = rhs.i; return *this; }
};

int main()
{
Foo f;
f++ = ++f;
}

With sample output:
ctor
preincrement
postincrement
copy ctor
copy ctor
dtor
assignment
dtor
dtor

Can someone explain to me why the preincrement is allowed to be
evaluated first? I thought that this would result in code (in
pseudocode) looking like f.operator++(int).operator=(f.operator++())
-- which would require the postincrement to go first? I mean, there's
a sequence point at the postincrement function call, right?

If I've got this wrong, can someone please explain to me the impact of
operator precedence vs. sequence points in evaluations such as this?

Thanks,
FL
 
V

Victor Bazarov

Consider the following,

[..]
int main()
{
Foo f;
f++ = ++f;

In this statement 'f' is changed and its value is used. That's
undefined behaviour last time I checked. Any conclusions you want
to derive from the output of your program are bogus, sorry.

V
 
F

feeblez

Consider the following,
[..]
int main()
{
Foo f;
f++ = ++f;

In this statement 'f' is changed and its value is used. That's
undefined behaviour last time I checked. Any conclusions you want
to derive from the output of your program are bogus, sorry.

Point taken, but the following generates the same output, so my
question still stands.

Foo f;
Foo f2;
f++ = ++f2;
 
R

red floyd

Consider the following,
[..]
int main()
{
Foo f;
f++ = ++f;
In this statement 'f' is changed and its value is used. That's
undefined behaviour last time I checked. Any conclusions you want
to derive from the output of your program are bogus, sorry.

Point taken, but the following generates the same output, so my
question still stands.

Foo f;
Foo f2;
f++ = ++f2;

I believe the Standard merely specifies the order in which the
operations shall occur, not the order in which the operands are evaluate.

i.e.: the two ++ operators will be evaluated before the assignment.
BUT the Standard says nothing about the order in which the two ++'s are
evaluated.

In other words, some implementations may do f++ first, others may do
++f2 first, but all implementations must do both of them before the
assignment.
 
M

Markus Schoder

Consider the following,

[..]
int main()
{
Foo f;
f++ = ++f;

In this statement 'f' is changed and its value is used. That's
undefined behaviour last time I checked. Any conclusions you want to
derive from the output of your program are bogus, sorry.

Wrong. This rule only applies to the built-in operators not user-defined
ones.

The program does not have undefined behaviour in the standard's sense but
the order of evaluation for the left hand side and right hand side of the
'=' is indeterminate and should not be relied upon.
 
O

Old Wolf

Consider the following,
[..]
int main()
{
Foo f;
f++ = ++f;

In this statement 'f' is changed and its value is used.
That's undefined behaviour last time I checked.

If you had checked against the C++ standard you would
have found that the behaviour was not undefined, as
there is a sequence point at the function call of
the operator++ function.

To the OP: order of evaluation is completely
unrelated to operator precedence.
 
F

feeblez

If you had checked against the C++ standard you would
have found that the behaviour was not undefined, as
there is a sequence point at the function call of
the operator++ function.

To the OP: order of evaluation is completely
unrelated to operator precedence.

But as I asked initially, doesn't this (with even an overloaded
assignment operator) boil down to f.operator++
(int).operator=(f2.operator++()) ?

I would have understood if it was operator=(f.operator++(int),
f2.operator++()), but with the overload -- it shouldn't be?
 
B

Bart van Ingen Schenau

Consider the following,
Can someone explain to me why the preincrement is allowed to be
evaluated first? I thought that this would result in code (in
pseudocode) looking like f.operator++(int).operator=(f.operator++())
-- which would require the postincrement to go first? I mean, there's
a sequence point at the postincrement function call, right?

If I've got this wrong, can someone please explain to me the impact of
operator precedence vs. sequence points in evaluations such as this?

Thanks,
FL

You have got it wrong insofar that sequence points don't give a full
ordering of sub-expressions in a more complex expression.
The sequence point at the call to a function affects only the
arguments (and function designator) for that call.

So, logic dictates that the post-increment must be called before the
assignment.
And the sequence point of the assignment operator dictates that the
pre-increment must be fully complete before the assignment.
But there is no such relation between the post- and pre-increment
calls. Therefor, the compiler can order them in any way it likes.

Bart v Ingen Schenau
 
F

feeblez

So, logic dictates that the post-increment must be called before the
assignment.
And the sequence point of the assignment operator dictates that the
pre-increment must be fully complete before the assignment.
But there is no such relation between the post- and pre-increment
calls. Therefor, the compiler can order them in any way it likes.

What about the postincrement? Am I wrong to assume that the call
expands to f.operator++(int).operator=(...), or is it simply
absolutely legal to evaluate the parameters of the assignment
operator, prior to eval'ing the expression providing an object on
which to call the op= ?
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

What about the postincrement? Am I wrong to assume that the call
expands to f.operator++(int).operator=(...), or is it simply
absolutely legal to evaluate the parameters of the assignment
operator, prior to eval'ing the expression providing an object on
which to call the op= ?

The expressions on both sides of the = should be evaluated before the =
operator is, however the order in which they are evaluated are not defined.
 
F

feeblez

The expressions on both sides of the = should be evaluated before the =
operator is, however the order in which they are evaluated are not defined.

Isn't the whole point of sequence points that they define that the
evaluation and all side effects of an expression is completed, before
the subsequent expression is evaluated? If the first function in the
expanded expression is f.operator++(int), shouldn't that go first?

- FL
 
V

Victor Bazarov

Isn't the whole point of sequence points that they define that the
evaluation and all side effects of an expression is completed, before
the subsequent expression is evaluated? If the first function in the
expanded expression is f.operator++(int), shouldn't that go first?

How is it "the first function in the expanded expression"? What makes
it "the first"? The sheer fact that it's written to the left? Which
one is first in this expression, 'a' or 'b':

function(a, b);

?

V
 
P

peter koch

Consider the following,

#include <iostream>
using namespace std;
class Foo
{
[snip]
};

int main()
{
Foo f;
f++ = ++f;
}

With sample output:
ctor
preincrement
postincrement
copy ctor
copy ctor
dtor
assignment
dtor
dtor

Can someone explain to me why the preincrement is allowed to be
evaluated first? I thought that this would result in code (in
pseudocode) looking like f.operator++(int).operator=(f.operator++())
-- which would require the postincrement to go first? I mean, there's
a sequence point at the postincrement function call, right?

There is no distinction between member and non-member functions, so
the call corresponds to
operator=(f.operator++(int),f.operator++())

That is a function taking two parameters. There is no rules as to in
what order parameters of a function are evaluated, so the compiler
might evaluate the first parameter first or - as in your case - the
second parameter first. It might even evaluate part the two parameters
intertwened.

/Peter
 
F

feeblez

There is no distinction between member and non-member functions, so
the call corresponds to
operator=(f.operator++(int),f.operator++())

Thanks, that was all I wanted to know. As I wrote in one of my earlier
posts, I would have understood if the global operator was called.
 
F

feeblez

How is it "the first function in the expanded expression"? What makes
it "the first"? The sheer fact that it's written to the left? Which
one is first in this expression, 'a' or 'b':

function(a, b);

Well that's what sequence points are there for: determining what
happens first. The simple explanation was "there's no difference
between the member op= and global op=", so I don't see how you're
contributing here. And to be absolutely clear, the order *is*
important, granted that there *are* sequence points there. Imagine
what if(foo() && bar()) would amount to otherwise ...
 
V

Victor Bazarov

Well that's what sequence points are there for: determining what
happens first. The simple explanation was "there's no difference
between the member op= and global op=", so I don't see how you're
contributing here. And to be absolutely clear, the order *is*
important, granted that there *are* sequence points there. Imagine
what if(foo() && bar()) would amount to otherwise ...

I am not sure what you're complaining about, and what does your &&
example have to do with the original expression. Sequence points
are well defined, they either exist or they don't. Order of the
argument evaluation is NOT defined. BTW, if operator&& is overloaded
for the return value types of 'foo()' and 'bar()' in your last
statement, the situation would be very different compared to the use
of the built-in operator && for bool.

Now, as to my "contribution", I hope I made you step back and think
just a little bit (don't overdo it, now, I don't want you to hurt
your head). Besided, you still didn't answer my questions.

V
 
F

feeblez

I am not sure what you're complaining about, and what does your &&
example have to do with the original expression. Sequence points
are well defined, they either exist or they don't. Order of the
argument evaluation is NOT defined.

You seemed to suggest that the order of the source code was
irrelevant, sequence points or no sequence points.

I think the fact of the matter here is that no proper explanation has
shown up until Peter commented. Sequence points *are* well defined,
and in their presence, the code *is* interpreted in a certain order. I
can't see a single person addressing the question "Am I wrong to
assume that the call expands to f.operator++(int).operator=(...)", the
answer to which would have made all the difference.

An evaluation such as "operator=(foo(), bar)" is something entirely
different from "foo().operator=(), bar"; so the interesting piece of
information was: *is* operator= evaluated as if global, or the member
overload that it is? That has been addressed. Now the followup is:
where is this behavior defined?
 
A

Alf P. Steinbach

* (e-mail address removed):
An evaluation such as "operator=(foo(), bar)" is something entirely
different from "foo().operator=(), bar";

Yes, but that's because the latter is invalid code: operator= takes an
argument, and you forgot to supply one.

Perhaps you meant that

operator=(foo(), bar);

is different from

foo().operator=( bar );

But no, not with respect to argument evaluation: from the compiler's
point of view both are function calls with two arguments, which can be
evaluated in any order.

so the interesting piece of
information was: *is* operator= evaluated as if global, or the member
overload that it is? That has been addressed. Now the followup is:
where is this behavior defined?

In the C++ standard.
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

You seemed to suggest that the order of the source code was
irrelevant, sequence points or no sequence points.

I think the fact of the matter here is that no proper explanation has
shown up until Peter commented. Sequence points *are* well defined,
and in their presence, the code *is* interpreted in a certain order. I
can't see a single person addressing the question "Am I wrong to
assume that the call expands to f.operator++(int).operator=(...)", the
answer to which would have made all the difference.

If you had quoted the rest of that question and then read my answer to
it you'd seen that you question was answered. The rest of your question was:

"or is it simply absolutely legal to evaluate the parameters of the
assignment operator, prior to eval'ing the expression providing an
object on which to call the op= ?"

And my answer:

"The expressions on both sides of the = should be evaluated before the =
operator is, however the order in which they are evaluated are not defined."
An evaluation such as "operator=(foo(), bar)" is something entirely
different from "foo().operator=(), bar"; so the interesting piece of
information was: *is* operator= evaluated as if global, or the member
overload that it is? That has been addressed. Now the followup is:
where is this behavior defined?

There is not such thing as "foo().operator=(), bar", I assume you meant
"foo().operator=(bar)". And as far as C++ is concerned that is exactly
the same thing as "operator=(foo(), bar)", operators are just a methods
with fancier syntax.
 
F

feeblez

* (e-mail address removed):

Yes, but that's because the latter is invalid code: operator= takes an
argument, and you forgot to supply one.

Yeah, slight mishap there.
Perhaps you meant that

operator=(foo(), bar);

is different from

foo().operator=( bar );

But no, not with respect to argument evaluation: from the compiler's
point of view both are function calls with two arguments, which can be
evaluated in any order.

Thanks for bringing that up. I had up until now been completely
oblivious to the fact that instances are in fact function parameters.
In the C++ standard.

Yes, good point, now that I'm down with the parameter part. Thanks for
clearing everything up.
 

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,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top