Oops! Comma operator is the hardest to understand in the C++ standard!

L

Lighter

In 5.3.3.4 of the standard, the standard provides that "The lvalue-to-
rvalue(4.1), array-to-pointer(4.2),and function-to-pointer(4.3)
standard conversions are not applied to the operand of sizeof."

I think this rule is easy to understand. Because I can find the
contexts of applying the rule as follows.

(1)
int* p = 0;
int b1 = sizeof(*p); // OK, b1 = 4, *p would not be evaluated.

(2)
int array[2];
int b2 = sizeof(array); // OK, b2 = 8, array will not be automatically
converted to a pointer to int

(3)
int b3 = sizeof(strlen); // error, strlen will not be automatically
converted to a function pointer.

So far, everything is understandable.
==========================================

However, In 5.18 of the standard, I find a similar rule on comma
operator: "The lvalue-to-rvalue(4.1), array-to-pointer(4.2),and
function-to-pointer(4.3) standard conversions are not applied to the
left expression."

I tried my best to guess the reason of the latter rule, but failed.
Even, I could not find an example to examine the rule.

(1)
int a, b;
a = b, a++; // compiling is OK. Why? Because a = b will cause an
lvalue-to-rvalue conversion.

(2)

int a[2];
int* b;
b = a, b++; // compiling is OK. Why? Because b = a will cause an array-
to-pointer conversion.

(3)
typedef int (*FP)(int, int);

int Hello(int a, int b)
{
return a + b;
}

FP pf;
pf = Hello, (*pf)(1, 2); // compiling is OK. Why? Because pf = Hello
will cause a function-to-pointer conversion.

I have been thinking the problem for several days, and now I feel lost
and clueless. Who can give me some tips? Any help will be highly
appreciated!
 
V

Victor Bazarov

Lighter said:
In 5.3.3.4 of the standard, the standard provides that "The lvalue-to-
rvalue(4.1), array-to-pointer(4.2),and function-to-pointer(4.3)
standard conversions are not applied to the operand of sizeof."

I think this rule is easy to understand. Because I can find the
contexts of applying the rule as follows.

(1)
int* p = 0;
int b1 = sizeof(*p); // OK, b1 = 4, *p would not be evaluated.

(2)
int array[2];
int b2 = sizeof(array); // OK, b2 = 8, array will not be automatically
converted to a pointer to int

(3)
int b3 = sizeof(strlen); // error, strlen will not be automatically
converted to a function pointer.

So far, everything is understandable.
==========================================

However, In 5.18 of the standard, I find a similar rule on comma
operator: "The lvalue-to-rvalue(4.1), array-to-pointer(4.2),and
function-to-pointer(4.3) standard conversions are not applied to the
left expression."

I tried my best to guess the reason of the latter rule, but failed.
Even, I could not find an example to examine the rule.

(1)
int a, b;
a = b, a++; // compiling is OK. Why? Because a = b will cause an
lvalue-to-rvalue conversion.

*Inside* the [assignment] expression on the left side of the comma
any conversion does not matter AFA 5.18 is concerned. However, as
the result of the assignment (with return value int&) '(a=b)' is NOT
onverted to r-value. That's what 5.18 is talking about.
(2)

int a[2];
int* b;
b = a, b++; // compiling is OK. Why? Because b = a will cause an
array- to-pointer conversion.

Again, you care safely rewrite this as

b = a;
b, b++;

it has _precisely_ the same effect. In the first (assignment)
expression statement, 'a' is converted to a pointer. However, at
the left side of the comma, 'b' (a reference to a pointer to int)
is not converted into anything.
(3)
typedef int (*FP)(int, int);

int Hello(int a, int b)
{
return a + b;
}

FP pf;
pf = Hello, (*pf)(1, 2); // compiling is OK. Why? Because pf = Hello
will cause a function-to-pointer conversion.

Same thing. You're confusing _where_ the conversion happens. The
operands of the comma operator are evaluated first, and during that
evaluation anything will happen what's supposed to happen. But right
after that evaluation, the result of that evaluation will NOT be
additionally subjected to lvalue-to-rvalue conversion.
I have been thinking the problem for several days, and now I feel lost
and clueless. Who can give me some tips? Any help will be highly
appreciated!

See above.

V
 
M

Markus Schoder

However, In 5.18 of the standard, I find a similar rule on comma
operator: "The lvalue-to-rvalue(4.1), array-to-pointer(4.2),and
function-to-pointer(4.3) standard conversions are not applied to the
left expression."

I tried my best to guess the reason of the latter rule, but failed.
Even, I could not find an example to examine the rule.

Since the value of the left expression of the comma operator is discarded
anyway there would be no purpose in converting it. I cannot even see how
one would check whether the conversion happens or not.
(1)
int a, b;
a = b, a++; // compiling is OK. Why? Because a = b will cause an
lvalue-to-rvalue conversion.

Here the left expression of the comma operator is "a = b" and is not
converted. What is converted is b which is the right hand side of an
assignment.
[... more examples of assignment rhs and comma left expression
confusion ...]
 
L

Lighter

Thank you very much, Victor.

As far as I know, C99 didn't provide the rule. My confusion lies in
the reason for making such a rule. Could you give me an example
illustrating that some conversions are applied on the left expression
(or the left operand)? I could not find such an example. If there were
no such an example. Why did the standard provide such a rule?

Thanks again.
 
V

Victor Bazarov

Lighter said:
Thank you very much, Victor.

As far as I know, C99 didn't provide the rule. My confusion lies in
the reason for making such a rule. Could you give me an example
illustrating that some conversions are applied on the left expression
(or the left operand)? I could not find such an example. If there were
no such an example. Why did the standard provide such a rule?

I do not know the reason for this rule. Please ask in 'comp.std.c++'
where they discuss and explain the rationales for the ways things are
in the Standard. IOW, we here discuss *how* to do things in C++, and
they there discuss *why*.

V
 
L

Lighter

Since the value of the left expression of the comma operator is discarded
anyway there would be no purpose in converting it. I cannot even see how
one would check whether the conversion happens or not.

Since one couldn't check whether the conversion happens or not, WHY
did the C++ standard explicitly provide such a rule which doesn't
exist in the C language standard? What confused me is just the
question.
 
J

John Harrison

Lighter said:
Since one couldn't check whether the conversion happens or not, WHY
did the C++ standard explicitly provide such a rule which doesn't
exist in the C language standard? What confused me is just the
question.

LOL, if a tree falls in the forest with no-one around to hear it, does
it make any sound?

Bishop Berkeley
 
J

jg

On Jun 22, 1:03 am, Markus Schoder <[email protected]> wrote:
Since one couldn't check whether the conversion happens or not, WHY
did the C++ standard explicitly provide such a rule which doesn't
exist in the C language standard? What confused me is just the
question.

You sure can check if your C++ compiler does the lvalue-to-rvalue
conversion. Try the following code, if it crashes, it does not;
otherwise, it does.

JG
-----------------------
#include <iostream>

int main()
{
int *pi=0;

int x = (*pi, 10);

std::cout << x << std::endl;
return 0;
}
 
C

Clark Cox

You sure can check if your C++ compiler does the lvalue-to-rvalue
conversion. Try the following code, if it crashes, it does not;
otherwise, it does.

Wrong. You can't conclude anything from the given code, as it invokes
undefined behavior.
JG
-----------------------
#include <iostream>

int main()
{
int *pi=0;

int x = (*pi,

At this point all bets are off, the program could crash, it could
assign 10 to x, it could let loose the proverbial nasal demons.
 
L

Lighter

You sure can check if your C++ compiler does the lvalue-to-rvalue
conversion. Try the following code, if it crashes, it does not;
otherwise, it does.

JG
-----------------------
#include <iostream>

int main()
{
int *pi=0;

int x = (*pi, 10);

std::cout << x << std::endl;
return 0;



}- Hide quoted text -

- Show quoted text -

I tried with VS 2005. It didn't crash. That means the compiler doesn't
perform lvalue-to-rvalue conversion.
 
V

Victor Bazarov

Lighter said:
I tried with VS 2005. It didn't crash. That means the compiler doesn't
perform lvalue-to-rvalue conversion.

No, it doesn't mean squat. Undefined behaviour is just that, undefined.
If the compiler [by not following the Standard] actually performs the
conversion, there is no way to tell by observing the undefined behaviour
of the program above.

V
 
J

James Kanze

First, I have to say that I like the subject line:). The comma
operator is a major misfeature in C++, and most of the coding
guidelines I've seen forbid its use. (A large part of the
problem, of course, is that the comma isn't always an operator.)
In 5.3.3.4 of the standard, the standard provides that "The lvalue-to-
rvalue(4.1), array-to-pointer(4.2),and function-to-pointer(4.3)
standard conversions are not applied to the operand of sizeof."
I think this rule is easy to understand. Because I can find the
contexts of applying the rule as follows.

[...]
However, In 5.18 of the standard, I find a similar rule on comma
operator: "The lvalue-to-rvalue(4.1), array-to-pointer(4.2),and
function-to-pointer(4.3) standard conversions are not applied to the
left expression."

The wording could, perhaps, be better. In this case, the "left
expression" refered to is the "expression" which appears to the
left of the comma operator in the production "expression ,
assignment-operator".
I tried my best to guess the reason of the latter rule, but failed.
Even, I could not find an example to examine the rule.
(1)
int a, b;
a = b, a++; // compiling is OK. Why? Because a = b will cause an
lvalue-to-rvalue conversion.

The expression "a = b" will not be subject to lvalue to rvalue
conversion. The expression "a = b" is not used (although it
must be evaluated for side effects). "b", of course, will be
subjected to an lvalue to rvalue conversion, but "b", by itself,
is NOT the expression to the left of the comma operator. The
expression (in the grammar production) to the left of the comma
operator is "a = b". That expression is an lvalue, and it will
not be subject to an lvalue to rvalue conversion.
int a[2];
int* b;
b = a, b++; // compiling is OK. Why? Because b = a will cause an array-
to-pointer conversion.

As above. The type of the expression "b = a" is int*; there's
no array to be converted in the expression to the left of the
comma operator.
(3)
typedef int (*FP)(int, int);
int Hello(int a, int b)
{
return a + b;
}
FP pf;
pf = Hello, (*pf)(1, 2); // compiling is OK. Why? Because pf = Hello
will cause a function-to-pointer conversion.

As above. The expression to the left of the comma is "pf =
Hello". It has type pointer to function, not type function.
I have been thinking the problem for several days, and now I
feel lost and clueless. Who can give me some tips? Any help
will be highly appreciated!

The word "expression" in "The lvalue-to-rvalue (4.1),
array-to-pointer (4.2), and function-to-pointer (4.3) standard
conversions are not applied to the left expression" refers to
the "expression" to the left of the comma in the grammar
production, not to any expression which happens to appear to the
left of the comma.

Globally, most of the time, it doesn't matter. There aren't
many ways you could tell. I can only think of three:

int a ;
a, f() ; // Not undefined, despite a not being
// initialized (thanks Victor).

extern int a[] ;
a, f() ; // No definition for a required, because
// no array to pointer conversion, and a
// still not considered used. (Not 100%
// sure on this one.)

volatile int a ;
a, f() ; // No "access" to a.

In practice, however, in almost all programs, it just doesn't
matter.
 
J

James Kanze

Thank you very much, Victor.
As far as I know, C99 didn't provide the rule.

In C99, it says "The left operand of a comma operator is
evaluated as a void expression". This more or less implies that
the lvalue to rvalue conversion cannot occur, because there are
no rvalues of type void. For the other conversions, C
explicitly says that they do occur in this case.

In practice, I don't think it makes a difference. I've
rechecked the standard, and my supposition that without the
array-to-pointer conversion, the object wasn't used, is wrong;
an object is used as soon as it appears in a potentially
evaluated expression (and only the operands of sizeof, and some
cases with typeid, and some cases of integral constant
expressions are not potentially evaluated expressions). And I
can't think of any other way of determining whether the
conversion occurs or not here.
 
J

James Kanze

No, it doesn't mean squat. Undefined behaviour is just that, undefined.
If the compiler [by not following the Standard] actually performs the
conversion, there is no way to tell by observing the undefined behaviour
of the program above.

And traditionally, dereferencing a null pointer is undefined
behavior, regardless of whether there is an lvalue to rvalue
conversion afterwards, or not. Something like &*p is undefined
behavior in p is a null pointer, according to the C++ standard.

About the only sure way I can think of is something like:

int volatile* p = someMemoryMappedIOAddress ;
*p, 10 ;

If there is an lvalue to rvalue conversion, the compiler will
(should) generate code to access the memory mapped IO, which is
observable behavior (in theory, anyway). Without the lvalue to
rvalue conversion, no access.
 
S

sss.zhou

(1)
int* p = 0;
int b1 = sizeof(*p); // OK, b1 = 4, *p would not be evaluated.
Using type int to illustrate this, I think is not a good example.
For in 64-bit system, b1 will be 8 .

double* p = 0;
int b1 = sizeof(*p); // b1 = 8, I did this in VS2005
 

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

Latest Threads

Top