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

Discussion in 'C++' started by Lighter, Jun 21, 2007.

  1. Lighter

    Lighter Guest

    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!
     
    Lighter, Jun 21, 2007
    #1
    1. Advertisements

  2. *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.
    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.
    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.
    See above.

    V
     
    Victor Bazarov, Jun 21, 2007
    #2
    1. Advertisements

  3. 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.
    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.
     
    Markus Schoder, Jun 21, 2007
    #3
  4. Lighter

    Lighter Guest

    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.
     
    Lighter, Jun 21, 2007
    #4
  5. 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
     
    Victor Bazarov, Jun 21, 2007
    #5
  6. Lighter

    Lighter Guest

    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.
     
    Lighter, Jun 21, 2007
    #6
  7. LOL, if a tree falls in the forest with no-one around to hear it, does
    it make any sound?

    Bishop Berkeley
     
    John Harrison, Jun 21, 2007
    #7
  8. Lighter

    jg Guest

    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;
    }
     
    jg, Jun 21, 2007
    #8
  9. Lighter

    Clark Cox Guest

    Wrong. You can't conclude anything from the given code, as it invokes
    undefined behavior.
    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.
     
    Clark Cox, Jun 21, 2007
    #9
  10. Lighter

    Lighter Guest

    I tried with VS 2005. It didn't crash. That means the compiler doesn't
    perform lvalue-to-rvalue conversion.
     
    Lighter, Jun 21, 2007
    #10
  11. 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
     
    Victor Bazarov, Jun 22, 2007
    #11
  12. Lighter

    James Kanze Guest

    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.)
    [...]
    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".
    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.
    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.
    As above. The expression to the left of the comma is "pf =
    Hello". It has type pointer to function, not type function.
    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.
     
    James Kanze, Jun 22, 2007
    #12
  13. Lighter

    James Kanze Guest

    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.
     
    James Kanze, Jun 22, 2007
    #13
  14. Lighter

    James Kanze Guest

    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.
     
    James Kanze, Jun 22, 2007
    #14
  15. Lighter

    Lighter Guest

    Lighter, Jun 22, 2007
    #15
  16. Lighter

    sss.zhou Guest

    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
     
    sss.zhou, Jun 24, 2007
    #16
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.