"An expression can result in a value..."

  • Thread starter Steven T. Hatton
  • Start date
S

Steven T. Hatton

I'm trying to improve my formal understanding of C++. One significant part
of that effort involves clarifying my understanding of the vocabulary used
to describe the language.

This is from the C++ Standard:

"[Note: Clause 5 defines the syntax, order of evaluation, and meaning of
expressions. An expression is a sequence of operators and operands that
specifies a computation. An expression can result in a value and
can cause side effects. ...]"

"...sequence of operators and operands that specifies a computation...".
That means to me that an expression can be "executed". I am purposely
avoiding the term "evaluate" because that has connotations which I am not
sure apply to all expressions. Nonetheless, the notion of a computation
signifies to me that some kind of predetermined state change takes place at
the time the flow of control reaches the point in the compiled program
corresponding to the /expression/ appearing in the source code. So my
first question is:

How might we formulate a definition of "computation" which communicates the
intended meaning in the above quoted excerpt?

My second question involves the exact meaning of "An expression can result
in a value...". The concept of 'resulting in a value' suggests there is
some storage location holding said value at the instant at which the
execution ("evaluation" probably applies here) of the expression is
complete. I note that the Standard does not specifically state that an
expression can /return/ a value. It seems to me that the resulting value
of a function returning a type other than void will be the value returned.
But what about values assigned to non-const parameters passed by reference?
Are these to be considered side effects, resulting values, or both?

In C++ parlance, is the value resulting from `int x, y;/*...*/ x + y;'
considered a "return value"? Is there ever an instance in which
the 'resulting value' of an expression cannot properly be called
its 'return value'?
 
E

Evan

Steven said:
I'm trying to improve my formal understanding of C++. One significant part
of that effort involves clarifying my understanding of the vocabulary used
to describe the language.

This is from the C++ Standard:

"[Note: Clause 5 defines the syntax, order of evaluation, and meaning of
expressions. An expression is a sequence of operators and operands that
specifies a computation. An expression can result in a value and
can cause side effects. ...]"

"...sequence of operators and operands that specifies a computation...".
That means to me that an expression can be "executed". I am purposely
avoiding the term "evaluate" because that has connotations which I am not
sure apply to all expressions. Nonetheless, the notion of a computation
signifies to me that some kind of predetermined state change takes place at
the time the flow of control reaches the point in the compiled program
corresponding to the /expression/ appearing in the source code. So my
first question is:

How might we formulate a definition of "computation" which communicates the
intended meaning in the above quoted excerpt?

My second question involves the exact meaning of "An expression can result
in a value...". The concept of 'resulting in a value' suggests there is
some storage location holding said value at the instant at which the
execution ("evaluation" probably applies here) of the expression is
complete. I note that the Standard does not specifically state that an
expression can /return/ a value. It seems to me that the resulting value
of a function returning a type other than void will be the value returned.
But what about values assigned to non-const parameters passed by reference?
Are these to be considered side effects, resulting values, or both?

In C++ parlance, is the value resulting from `int x, y;/*...*/ x + y;'
considered a "return value"? Is there ever an instance in which
the 'resulting value' of an expression cannot properly be called
its 'return value'?

I'm not sure that I'm better off in my understanding than you, so what
I'm saying might be blatently obvious to you. (Or possibly wrong...)

But my understanding is that there's not a fundamental difference
between what you're thinking of a return value and what the standard
calls resulting in a value. 5+6; doesn't return a value, because it
doesn't *return* anything. "Return" is a statement about control flow.
(There might be a value attached to it, as in a function returning an
int, but the point is that it at least has a substantial part that is
control flow.)
But what about values assigned to non-const parameters passed by reference?
Are these to be considered side effects, resulting values, or both?

Are you saying that on the marked line:

int foo(xnt& x) { x=1; return 2; }
int bar() {
int a;
foo(a) + 5; // <= here
}

the change in value of a is considered a side effect, resulting value,
or both?

If so, that's definitely just a side effect. The resulting value of
'foo(a)' should be 2, and then of the whole expression statement, 7.

Evan
 
S

Steve Pope

Steven T. Hatton said:
In C++ parlance, is the value resulting from `int x, y;/*...*/ x + y;'
considered a "return value"? Is there ever an instance in which
the 'resulting value' of an expression cannot properly be called
its 'return value'?

I only use "return value" for the value returned by a function.

Others may use the phrase differently.

Steve
 
S

Steven T. Hatton

Evan said:
I'm trying to improve my formal understanding of C++. One significant
part of that effort involves clarifying my understanding of the
vocabulary used to describe the language.

This is from the C++ Standard:

"[Note: Clause 5 defines the syntax, order of evaluation, and meaning of
expressions. An expression is a sequence of operators and operands that
specifies a computation. An expression can result in a value and
can cause side effects. ...]"

"...sequence of operators and operands that specifies a computation...".
That means to me that an expression can be "executed". I am purposely
avoiding the term "evaluate" because that has connotations which I am not
sure apply to all expressions. Nonetheless, the notion of a computation
signifies to me that some kind of predetermined state change takes place
at the time the flow of control reaches the point in the compiled program
corresponding to the /expression/ appearing in the source code. So my
first question is:

How might we formulate a definition of "computation" which communicates
the intended meaning in the above quoted excerpt?

My second question involves the exact meaning of "An expression can
result
in a value...". The concept of 'resulting in a value' suggests there is
some storage location holding said value at the instant at which the
execution ("evaluation" probably applies here) of the expression is
complete. I note that the Standard does not specifically state that an
expression can /return/ a value. It seems to me that the resulting value
of a function returning a type other than void will be the value
returned. But what about values assigned to non-const parameters passed
by reference? Are these to be considered side effects, resulting values,
or both?

In C++ parlance, is the value resulting from `int x, y;/*...*/ x + y;'
considered a "return value"? Is there ever an instance in which
the 'resulting value' of an expression cannot properly be called
its 'return value'?

I'm not sure that I'm better off in my understanding than you, so what
I'm saying might be blatently obvious to you. (Or possibly wrong...)

But my understanding is that there's not a fundamental difference
between what you're thinking of a return value and what the standard
calls resulting in a value. 5+6; doesn't return a value, because it
doesn't *return* anything. "Return" is a statement about control flow.
(There might be a value attached to it, as in a function returning an
int, but the point is that it at least has a substantial part that is
control flow.)

It's been a long time since I went through the details of how an activation
stack works, but IIRC, the instructions resulting from compiling an
expression are put on the stack along with any automatic variables.
Variables which are visible from outside the code being executed have to be
referred to by their addresses. Any value "returned" by a function must be
placed in storage which can be accessed after the function returns. In
this context "returning" means the instruction pointer has reached the last
instruction of the function, and then assumed the address of the subsequent
instruction.

So, yes, there is a significant component of control flow involved in the
notion of "returning". It seems reasonable to conclude that the address
range into which the return value (if any) will be placed must be
established prior to execution of the function producing the value.

I'm not sure how the address of a function used to initialize a pointer to
function relates to the storage location of a return value.

I do believe the notion of "returning" is applicable to arithmetic
expressions such as int x, y; x+y; I'm not sure how it might differ from
the branch and return that takes place with a user written function call.
One thing to keep in mind with C++ is that there is a notion of returning
references which makes me cautious about jumping to any conclusions about
what it means to have a return value. I'm also not sure what, if any,
differences exist between the execution time characteristics of a storage
location used for a return value and the storage location used for a
variable parameter.

My inclination is to think of 'return value' and 'resulting value' as
synonymous, but this is C++. Anything is possible.
Are you saying that on the marked line:

int foo(xnt& x) { x=1; return 2; }
int bar() {
int a;
foo(a) + 5; // <= here
}

the change in value of a is considered a side effect, resulting value,
or both?

If so, that's definitely just a side effect. The resulting value of
'foo(a)' should be 2, and then of the whole expression statement, 7.

My inclination is to agree, but, as I said, this is C++.
 
E

Evan

It's been a long time since I went through the details of how an activation
stack works, but IIRC, the instructions resulting from compiling an
expression are put on the stack along with any automatic variables.

I'm not sure what you mean here. Instructions don't go on the stack.
(In fact, in an ideal world, the stack would be marked no execute.) Do
you mean the intermediate computation results?
Variables which are visible from outside the code being executed have to be
referred to by their addresses. Any value "returned" by a function must be
placed in storage which can be accessed after the function returns. In
this context "returning" means the instruction pointer has reached the last
instruction of the function, and then assumed the address of the subsequent
instruction.

So, yes, there is a significant component of control flow involved in the
notion of "returning". It seems reasonable to conclude that the address
range into which the return value (if any) will be placed must be
established prior to execution of the function producing the value.

That depends on what you mean by "established". If you mean known, then
yes. But if you mean "here's the return value spot", this isn't usually
how it's done. For instance, on x86 and the cdecl calling convention,
the return value spot is usually the same as either one of the local
variables or a spot for a saved register. With stdcall, I think it
shares a spot with one of the parameters. Either way, there's not a
dedicated "here's where the return value goes" location that's set
aside for that purpose. Then of course there are some architectures
(SPARC for instance) where standard calling conventions place the
return value in a register.

But what happens with the return value is secondary to my point, which
is that the standard is probably reserving "return" for the control
flow effects.

(Stuff like short-circuiting operators sorta throw a wrench into my
"expressions don't have control flow so don't return" argument, but
that's still the idea. Return is control flow from a function.)
I'm not sure how the address of a function used to initialize a pointer to
function relates to the storage location of a return value.

It doesn't.

I do believe the notion of "returning" is applicable to arithmetic
expressions such as int x, y; x+y;

I don't fault your use of 'return' there; I've used that myself.
("Postfix increment returns the value before the change.") But I think
that the standard is making that distinction, and why they don't use
return.
I'm not sure how it might differ from
the branch and return that takes place with a user written function call.

Because there's no control flow. (Again, short circuits throw a wrench
into this definition, but there's still no control flow away from a
function.)
One thing to keep in mind with C++ is that there is a notion of returning
references which makes me cautious about jumping to any conclusions about
what it means to have a return value.

What's special about references? It's just returning an address.
I'm also not sure what, if any,
differences exist between the execution time characteristics of a storage
location used for a return value and the storage location used for a
variable parameter.

Modulo architecture differences (SPARC puts these values into different
registers, etc.), not really anything. Both get placed on the stack in
the current activation record. Again in x86, the caller pushes the
function onto the stack, the callee pushes the return value onto the
stack, and the caller pops the return value off. (Who pops the
arguments depends on the calling convention. In cdecl, the caller pops,
while in stdcall the callee pops. There's also fastcall which puts some
parms in registers.)

Evan
 
K

Kai-Uwe Bux

Steven said:
I'm trying to improve my formal understanding of C++. One significant
part of that effort involves clarifying my understanding of the vocabulary
used to describe the language.

This is from the C++ Standard:

"[Note: Clause 5 defines the syntax, order of evaluation, and meaning of
expressions. An expression is a sequence of operators and operands that
specifies a computation. An expression can result in a value and
can cause side effects. ...]"

"...sequence of operators and operands that specifies a computation...".
That means to me that an expression can be "executed". I am purposely
avoiding the term "evaluate" because that has connotations which I am not
sure apply to all expressions.

The standard uses the term "evaluate".
Nonetheless, the notion of a computation
signifies to me that some kind of predetermined state change takes place
at the time the flow of control reaches the point in the compiled program
corresponding to the /expression/ appearing in the source code. So my
first question is:

How might we formulate a definition of "computation" which communicates
the intended meaning in the above quoted excerpt?

The word "computation" occurs about four times in the standard and is
clearly used loosely. The closest you get is model of the the abstract
machine [1.9].
My second question involves the exact meaning of "An expression can result
in a value...". The concept of 'resulting in a value' suggests there is
some storage location holding said value at the instant at which the
execution ("evaluation" probably applies here) of the expression is
complete.

Nope, it does not suggest anything like that.
I note that the Standard does not specifically state that an
expression can /return/ a value. It seems to me that the resulting value
of a function returning a type other than void will be the value returned.
Correct.

But what about values assigned to non-const parameters passed by
reference? Are these to be considered side effects, resulting values, or
both?

No. But that answer relies on a very pedantic reading of the question.

In C++ parlance, is the value resulting from `int x, y;/*...*/ x + y;'
considered a "return value"? Is there ever an instance in which
the 'resulting value' of an expression cannot properly be called
its 'return value'?

As far as I know, the standard uses the term return value only for
functions, operator functions, and conversions. Built in operators are none
of those.


Best

Kai-Uwe Bux
 
S

Steven T. Hatton

Evan said:
I'm not sure what you mean here. Instructions don't go on the stack.
(In fact, in an ideal world, the stack would be marked no execute.) Do
you mean the intermediate computation results?

You are correct. The only thing that typically goes on the stack is the
return address. I was mixing paradigms. In Mathematica operators and
operands are treated on a peer. (more or less). For example, a recursive
function such as this simple implementation of factorial will push
everything onto a stack and then evaluate it as the stack pops.

f[1] = 1;
f[n_] := f[n - 1]n
Trace[f[4]]

{f[4],
f[4 - 1] 4,
{
{4 - 1, 3},
f[3],
f[3 - 1] 3,
{
{3 - 1, 2},
f[2], f[2 - 1] 2,
{
{2 - 1, 1},
f[1], 1
},
1 2, 2
},
2 3, 6
},
6 4, 24
}


I am a bit confused about what goes on with inline functions. I had been
given to believe they go "on the stack" but upon further investigation,
that appears incorrect.
That depends on what you mean by "established". If you mean known, then
yes. But if you mean "here's the return value spot", this isn't usually
how it's done. For instance, on x86 and the cdecl calling convention,
the return value spot is usually the same as either one of the local
variables or a spot for a saved register. With stdcall, I think it
shares a spot with one of the parameters. Either way, there's not a
dedicated "here's where the return value goes" location that's set
aside for that purpose. Then of course there are some architectures
(SPARC for instance) where standard calling conventions place the
return value in a register.

What I really meant is that there is some kind of activation record format,
and the address of the return value is established as an offset relative to
the starting address of that record.
But what happens with the return value is secondary to my point, which
is that the standard is probably reserving "return" for the control
flow effects.

(Stuff like short-circuiting operators sorta throw a wrench into my
"expressions don't have control flow so don't return" argument, but
that's still the idea. Return is control flow from a function.)

They use the term "returning" when talking about functions returning values.
They also use terminology talking about the "type" of an expression, which
I am inclined to understand as the "type" of the value it returns. I will
have to look more closely at the discussion of functions before I draw any
hard and fast conclusions.
I don't fault your use of 'return' there; I've used that myself.
("Postfix increment returns the value before the change.") But I think
that the standard is making that distinction, and why they don't use
return.

I have not seen the term used in the context of arithmetic operations
performed on builtin types. I'm not sure what the significance of that is.

What's special about references?

I'm not sure. I'm just being cautious.
It's just returning an address.

My understanding is that references don't really exist at runtime in the
same way that pointers, for example, exist.
Modulo architecture differences (SPARC puts these values into different
registers, etc.), not really anything. Both get placed on the stack in
the current activation record. Again in x86, the caller pushes the
function onto the stack, the callee pushes the return value onto the
stack, and the caller pops the return value off. (Who pops the
arguments depends on the calling convention. In cdecl, the caller pops,
while in stdcall the callee pops. There's also fastcall which puts some
parms in registers.)

Now I'm confused again. What exactly does it mean to push the function onto
the stack? Do you mean the activation record of the function? I would
expect that to hold the return value location. How the address of that
return value is communicated to the caller is not clear. But this really
goes beyond what I am trying to address right now.
 
S

Steven T. Hatton

Kai-Uwe Bux said:
Steven said:
I'm trying to improve my formal understanding of C++. One significant
part of that effort involves clarifying my understanding of the
vocabulary used to describe the language.

This is from the C++ Standard:

"[Note: Clause 5 defines the syntax, order of evaluation, and meaning of
expressions. An expression is a sequence of operators and operands that
specifies a computation. An expression can result in a value and
can cause side effects. ...]"

"...sequence of operators and operands that specifies a computation...".
That means to me that an expression can be "executed". I am purposely
avoiding the term "evaluate" because that has connotations which I am not
sure apply to all expressions.

The standard uses the term "evaluate".

I believe the term evaluate should be reserved for actions which actually
result in a value. Of course we can then haggle over whether a value which
is neither preserved nor ever used is actually the product of an
evaluation, since we never really "find the value". One could also argue
that all state changes are changes in value, so it would be hard for me to
argue very forcefully for my usage. I prefer, however, to refrain from
using the term "evaluate" when talking about dereferencing a pointer, for
example.
Nope, it does not suggest anything like that.

Then what is a value? And what does it mean to "result in a value"?
No. But that answer relies on a very pedantic reading of the question.

I would have considered the modification of a variable parameter to be a
side effect. Do you not agree with that?
As far as I know, the standard uses the term return value only for
functions, operator functions, and conversions. Built in operators are
none of those.

That's how it looks to me. I wonder if that is a conscious distinction or
simply the way things happened to turn out.
 
K

Kai-Uwe Bux

Steven said:
Kai-Uwe Bux said:
Steven said:
I'm trying to improve my formal understanding of C++. One significant
part of that effort involves clarifying my understanding of the
vocabulary used to describe the language.

This is from the C++ Standard:

"[Note: Clause 5 defines the syntax, order of evaluation, and meaning of
expressions. An expression is a sequence of operators and operands that
specifies a computation. An expression can result in a value and
can cause side effects. ...]"

"...sequence of operators and operands that specifies a computation...".
That means to me that an expression can be "executed". I am purposely
avoiding the term "evaluate" because that has connotations which I am
not sure apply to all expressions.

The standard uses the term "evaluate".

I believe the term evaluate should be reserved for actions which actually
result in a value. Of course we can then haggle over whether a value
which is neither preserved nor ever used is actually the product of an
evaluation, since we never really "find the value". One could also argue
that all state changes are changes in value, so it would be hard for me to
argue very forcefully for my usage. I prefer, however, to refrain from
using the term "evaluate" when talking about dereferencing a pointer, for
example.
Nope, it does not suggest anything like that.

Then what is a value?

A value is an lvalue or an rvalue. For rvalues, there is no guarantee that
they are physically represented in memory.
And what does it mean to "result in a value"?

It means that the type of the expression is not void and the evaluation of
the expression does not invoke undefined behavior.

I would have considered the modification of a variable parameter to be a
side effect. Do you not agree with that?

I do agree with that, but the modification of the parameter is not the same
as the value that the parameter gets. The value maybe the number 5. The
number 5, by and in itself, is not a side effect; the side effect is that a
certain location in memory now will have 5 stored in it. If that memory
location was different, say because you passed a different parameter, the
side effect would be different even though the value might still be 5.
Since the value did not change but the side effect did change, the side
effect and the value cannot be identical.

That's how it looks to me. I wonder if that is a conscious distinction or
simply the way things happened to turn out.

It is somewhat consistent: return values stem from return statements. I
would not read too much into it.


Best

Kai-Uwe Bux
 
S

Steven T. Hatton

Kai-Uwe Bux said:
A value is an lvalue or an rvalue. For rvalues, there is no guarantee that
they are physically represented in memory.

That really doesn't answer the question. I'm not coming up with an example
of an rvalue who's value doesn't exist, at some time, in memory. I believe
there may be instances of such entities but nothing is coming to mind.
Nonetheless, why should such an entity be considered a value?
It means that the type of the expression is not void and the evaluation of
the expression does not invoke undefined behavior.

Does it really? Suppose there is some block of code which the compiler can
determine will not impact the observable result of running the program as
required by the Standard. The compiler can omit the code from the
executable program. Is it really meaningful to say that, given such a
circumstance, the expression was evaluated and a value resulted?
I do agree with that, but the modification of the parameter is not the
same as the value that the parameter gets. The value maybe the number 5.
The number 5, by and in itself, is not a side effect; the side effect is
that a certain location in memory now will have 5 stored in it. If that
memory location was different, say because you passed a different
parameter, the side effect would be different even though the value might
still be 5. Since the value did not change but the side effect did change,
the side effect and the value cannot be identical.

My statement predicated that the value was assigned to a particular
non-const parameter.
It is somewhat consistent: return values stem from return statements. I
would not read too much into it.

I will never find a nuance in C++ that I do not hold suspect.
 
K

Kai-Uwe Bux

Steven said:
That really doesn't answer the question.

The answer is sufficient to address the misunderstanding that you snipped.
You are taking your question out of context; and now the answer might look
insufficient.
I'm not coming up with an example
of an rvalue who's value doesn't exist, at some time, in memory. I
believe there may be instances of such entities but nothing is coming to
mind.

E.g.:

unsigned int a = 5;
unsigned int b = 3;
// some code
unsigned int c = b + ( a + 7 );

There is no guarantee that a+7 will be in memory at any time.
Nonetheless, why should such an entity be considered a value?

Where in the standard do you find that rvalues not residing in memory are
not values?
Does it really? Suppose there is some block of code which the compiler can
determine will not impact the observable result of running the program as
required by the Standard. The compiler can omit the code from the
executable program. Is it really meaningful to say that, given such a
circumstance, the expression was evaluated and a value resulted?

I would maintain that the phrase "and the evaluation of the expression does
not invoke undefined behavior" from my answer stipulates that the
expression is being evaluated. However, if you insist, I can make this more
explicit:

An expression results in a value if its type is not void, it is evaluated,
and the evaluation does not invoke undefined behavior.

Anyway, in the case you describe where the compiler optimizes away an
expression, the standard still describes the situation as: the expression
is potentially evaluated. The as-if-rule only kicks in after the meaning of
the program (observable behavior) has been established.

My statement predicated that the value was assigned to a particular
non-const parameter.

a) Your statement was a question.
b) Your question did not ask whether the assignment of a value to a
non-const parameter is a side effect but whether the assigned value is.
Since you seem to be uncertain about what values are, it seemed appropriate
to point out that there is a difference between an assignment and the
assigned value.

[snip]


Best

Kai-Uwe Bux
 
S

Steven T. Hatton

Kai-Uwe Bux said:
The answer is sufficient to address the misunderstanding that you snipped.
You are taking your question out of context; and now the answer might look
insufficient.


E.g.:

unsigned int a = 5;
unsigned int b = 3;
// some code
unsigned int c = b + ( a + 7 );

There is no guarantee that a+7 will be in memory at any time.


Where in the standard do you find that rvalues not residing in memory are
not values?

How is the above situation different from any other "as if" allowance? What
the Standard specifies is an abstract machine which behaves as described.
The fact that some steps in the calculations can be omitted or consolidated
does not mean that the step is not specified in the abstract machine.
I would maintain that the phrase "and the evaluation of the expression
does not invoke undefined behavior" from my answer stipulates that the
expression is being evaluated. However, if you insist, I can make this
more explicit:

An expression results in a value if its type is not void, it is
evaluated, and the evaluation does not invoke undefined behavior.

So if it throws an exception it results in a value?
a) Your statement was a question.
b) Your question did not ask whether the assignment of a value to a
non-const parameter is a side effect but whether the assigned value is.
Since you seem to be uncertain about what values are, it seemed
appropriate to point out that there is a difference between an assignment
and the assigned value.

The fact that the given memory location holds the assigned value is the
effect of the assignment, which is the cause.
 
K

Kai-Uwe Bux

Steven said:
How is the above situation different from any other "as if" allowance?
What the Standard specifies is an abstract machine which behaves as
described. The fact that some steps in the calculations can be omitted or
consolidated does not mean that the step is not specified in the abstract
machine.

I was not talking about the as-if rule: where do you find the requirement
that the intermediate values of a computation have to reside in the memory
of the abstract machine? Maybe there is such a requirement, but I do not
know about it. The question really is whether for all evaluated expressions
an object is created: Memory is that part of the abstract machine where
objects reside and where it makes sense to take addresses. I thought,
addresses of rvalues may not be taken because they need not have a location
in the memory even of the abstract machine. However, I could be proven
wrong by a quote from the standard.

So if it throws an exception it results in a value?


The fact that the given memory location holds the assigned value is the
effect of the assignment, which is the cause.

True; and neither the memory location nor the act of assignment _is_ a
value. However, you again snipped a part which shows a misunderstanding
that I was addressing. Without that context, it is pointless to continue
this exchange.


Best

Kai-Uwe Bux
 
S

Steven T. Hatton

Kai-Uwe Bux said:
I was not talking about the as-if rule: where do you find the requirement
that the intermediate values of a computation have to reside in the memory
of the abstract machine? Maybe there is such a requirement, but I do not
know about it. The question really is whether for all evaluated
expressions an object is created: Memory is that part of the abstract
machine where objects reside and where it makes sense to take addresses. I
thought, addresses of rvalues may not be taken because they need not have
a location in the memory even of the abstract machine. However, I could be
proven wrong by a quote from the standard.

I was talking about the as-if rule.

The concept of an rvalues is only meaningful in terms of being assignable to
lvalues. That, in itself is an as-if. To say that some computation
results in an rvalue is to say that its value could be assigned to an
lvalue. To say that the value actually resulted from the computation
requires that there was some storage location holding the binary
representation of that value.
 
K

Kai-Uwe Bux

Steven said:
I was talking about the as-if rule.

The concept of an rvalues is only meaningful in terms of being assignable
to
lvalues. That, in itself is an as-if. To say that some computation
results in an rvalue is to say that its value could be assigned to an
lvalue. To say that the value actually resulted from the computation
requires that there was some storage location holding the binary
representation of that value.

I agree that there is a memory location holding the value *after* it has
been assigned to an lvalue. You seem to make the claim that it has to be in
memory (of the abstract machine) *before* it is assigned to an lvalue or
even if it so happens that it will never be assigned to an lvalue. If that
is your claim, I would like to see Chapter and Verse.


Best

Kai-Uwe Bux
 
E

Evan

I should start off with another disclaimer that I don't know how much
the standard actually talks about these things and how much are just
implementation artifacts. However, my understanding is that essentially
all implementations would work this way, so I don't feel that my
answers are particularly off-topic.
I am a bit confused about what goes on with inline functions. I had been
given to believe they go "on the stack" but upon further investigation,
that appears incorrect.

Inlined functions* have no control flow when actually executing, apart
from whatever is in the body of the function itself. In other words,
there's no CALL (JAL, whatever your ISA calls it) instruction to jump
to the function. The compiler places a copy of the function body at the
call site and rewrites variable names to match. For instance,

char a[] = "Hello world";
cout << "The string \"" << a << "\" has " << strlen(a) << "
characters\n";

would, if the compiler decided to inline the strlen call, have the same
effect as if you wrote something like

char a[] = "Hello world";
char* end = a;
while(end) ++end;
int ret = end-a;
cout << "The string \"" << a << "\" has " << ret << " characters\n";

There're, of course, some things that you have take into consideration
when doing this such as making sure destructors run at the proper time.
But the point is that if you look at the (disassembled presumably ;-))
object code for whatever function contains this code snippet, you won't
see the control flow leaving that function.

* The addition of the 'd' here is important for disambiguation; this
refers to call sites of functions that the compiler actually chose to
inline, which are not necessarily one-to-one with call sites of
functions that are marked inline. The compiler is free to ignore the
inline modifier and outline the code, or inline functions that are not
so marked.

What I really meant is that there is some kind of activation record format,
and the address of the return value is established as an offset relative to
the starting address of that record.


They use the term "returning" when talking about functions returning values.
They also use terminology talking about the "type" of an expression, which
I am inclined to understand as the "type" of the value it returns. I will
have to look more closely at the discussion of functions before I draw any
hard and fast conclusions.


I have not seen the term used in the context of arithmetic operations
performed on builtin types. I'm not sure what the significance of that is.



I'm not sure. I'm just being cautious.


My understanding is that references don't really exist at runtime in the
same way that pointers, for example, exist.

I would say that they don't really exist in the code in the same way
that pointers exist, because references have no identity apart from
what they are referencing. However, references are essentially just
syntactic sugar for pointers*, and that's how implementations treat
them. For instance, GCC generates the *exact* same code, except for the
mangled name of f, for
void f(int& x) { x=5; }
int main() { int a; f(a); return a; }
as it does for
void f(int* x) {*x=5; }
int main() { int a; f(&a); return a; }
(And this is with optimization off, so it's not inlining the call to f
or something.)

The compiler is also free to elide references. For instance:
int a;
int &r = a;
In this, I suspect that, at least with optimization on, the compiler
would unify r and a, and uses of r later in that function would be
replaced with uses of a. But then again, if you write:
int a;
int * const p = &a;
the compiler would also be free to replace uses of *p with a and elide
p entirely.


* I don't mean the syntactic sugar comment in a bad way either, it's
just a comment. I like sugar.
Now I'm confused again. What exactly does it mean to push the function onto
the stack? Do you mean the activation record of the function?

Yes.

(Though again, the function doesn't get pushed to the stack.)
I would expect that to hold the return value location. How the address of that
return value is communicated to the caller is not clear.

If you're going for an understanding of the C++ standard, it will be
silent on that issue more than likely. It's an implementation detail.
(And it's not even standardized; again, where it goes relative to the
stack pointer, frame pointer, and registers depends on your
architectuer and calling convention.)

Evan
 
S

Steven T. Hatton

Kai-Uwe Bux said:
I agree that there is a memory location holding the value *after* it has
been assigned to an lvalue. You seem to make the claim that it has to be
in memory (of the abstract machine) *before* it is assigned to an lvalue
or even if it so happens that it will never be assigned to an lvalue. If
that is your claim, I would like to see Chapter and Verse.

I am saying that the phrase "results in a value" connotes that the resulting
value exists in storage at the end of the computation represented by the
expression. The term rvalue is a grammatical concept which designates the
role played by certain constructs of the language. A value is a pattern of
bits stored in a specific location in the abstract machine.
 
K

Kai-Uwe Bux

Steven said:
I am saying that the phrase "results in a value" connotes that the
resulting value exists in storage at the end of the computation
represented by the expression.

And I say that connotation exists in your head and is not supported by the
language of the standard.
The term rvalue is a grammatical concept which designates the
role played by certain constructs of the language. A value is a pattern
of bits stored in a specific location in the abstract machine.

Chapter and verse, please.


Best

Kai-Uwe Bux
 
S

Steve Pope

Steven T. Hatton said:
Kai-Uwe Bux wrote:
I am saying that the phrase "results in a value" connotes that the resulting
value exists in storage at the end of the computation represented by the
expression.

Correct. Note that the standard says the expression "can result
in a value". It does not say it "shall result in a value".
The possibility is left open that it doesn't result in a value.
"Can" is not an imperative.

Steve
 
K

Kai-Uwe Bux

Steve said:

Really? If you have unsigned int variables 'a' and 'b', and you do

b = 3 + ( a + 7 );

then a+7 is an expression in your program that, according to my
understanding, results in a value. Could you give me a quote from the
standard that requires this result to be represented in the memory of the
abstract machine. I agree that the abstract machine has to compute the bit
pattern representing the value a+7 before it moves on to compute the bit
pattern for 3+(a+7). However, I would like to see where the standard
requires that the bit pattern for a+7 be stored in the memory of the
abstract machine. Why can't the abstract machine have registers?

In fact, for floating point operations, the standard explicitly allows the
results of expressions to be represented in greater precision than required
by the type [5/9]. If that happens, the result of a floating point
expression is not stored in an object of the corresponding type as that
region of memory would be too small to hold it. The result gets truncated
or rounded when committed to memory.
Note that the standard says the expression "can result
in a value". It does not say it "shall result in a value".
The possibility is left open that it doesn't result in a value.
"Can" is not an imperative.


Best

Kai-Uwe Bux
 

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
473,774
Messages
2,569,598
Members
45,152
Latest member
LorettaGur
Top