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. Advertising

  2. Lighter wrote:
    > 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
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Jun 21, 2007
    #2
    1. Advertising

  3. Re: Oops! Comma operator is the hardest to understand in the C++standard!

    On Thu, 21 Jun 2007 09:15:01 -0700, Lighter wrote:
    > 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 ...]


    --
    Markus Schoder
     
    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. Lighter wrote:
    > 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
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Jun 21, 2007
    #5
  6. Lighter

    Lighter Guest

    On Jun 22, 1:03 am, Markus Schoder <> wrote:
    > 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.
     
    Lighter, Jun 21, 2007
    #6
  7. Re: Oops! Comma operator is the hardest to understand in the C++standard!

    Lighter wrote:
    > On Jun 22, 1:03 am, Markus Schoder <> wrote:
    >> 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.
    >
    >


    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

    On Jun 21, 10:19 am, Lighter <> wrote:
    > On Jun 22, 1:03 am, Markus Schoder <> 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;
    }
     
    jg, Jun 21, 2007
    #8
  9. Lighter

    Clark Cox Guest

    On 2007-06-21 15:15:18 -0700, jg <> said:

    > On Jun 21, 10:19 am, Lighter <> wrote:
    >> On Jun 22, 1:03 am, Markus Schoder <> 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.


    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.

    > 10);
    >
    > std::cout << x << std::endl;
    > return 0;
    > }



    --
    Clark S. Cox III
     
    Clark Cox, Jun 21, 2007
    #9
  10. Lighter

    Lighter Guest

    On Jun 22, 6:15 am, jg <> wrote:
    > On Jun 21, 10:19 am, Lighter <> wrote:
    >
    > > On Jun 22, 1:03 am, Markus Schoder <> 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;
    >
    >
    >
    > }- 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.
     
    Lighter, Jun 21, 2007
    #10
  11. Lighter wrote:
    > On Jun 22, 6:15 am, jg <> wrote:
    >> On Jun 21, 10:19 am, Lighter <> wrote:
    >>
    >>> On Jun 22, 1:03 am, Markus Schoder <> 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;
    >>
    >>
    >>
    >> }- 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.


    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
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Jun 22, 2007
    #11
  12. Lighter

    James Kanze Guest

    On Jun 21, 6:15 pm, Lighter <> wrote:

    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.

    > (2)


    > 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.

    --
    James Kanze (GABI Software, from CAI) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jun 22, 2007
    #12
  13. Lighter

    James Kanze Guest

    On Jun 21, 7:08 pm, Lighter <> wrote:
    > 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.

    --
    James Kanze (GABI Software, from CAI) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jun 22, 2007
    #13
  14. Lighter

    James Kanze Guest

    On Jun 22, 2:00 am, "Victor Bazarov" <> wrote:
    > Lighter wrote:
    > >> -----------------------
    > >> #include <iostream>

    >
    > >> int main()
    > >> {
    > >> int *pi=0;

    >
    > >> int x = (*pi, 10);

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


    > >> }


    > > 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.


    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 (GABI Software, from CAI) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jun 22, 2007
    #14
  15. Lighter

    Lighter Guest

    Lighter, Jun 22, 2007
    #15
  16. Lighter

    Guest


    > (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
     
    , Jun 24, 2007
    #16
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. s.subbarayan
    Replies:
    2
    Views:
    548
    Francois Grieu
    Jul 5, 2004
  2. dorayme
    Replies:
    12
    Views:
    677
    dorayme
    Aug 15, 2007
  3. Ramon F Herrera
    Replies:
    0
    Views:
    359
    Ramon F Herrera
    Oct 2, 2007
  4. Replies:
    7
    Views:
    1,218
    Thomas Stanka
    Jan 20, 2008
  5. Helmut Jarausch

    python3 - the hardest hello world ever ?

    Helmut Jarausch, Oct 14, 2008, in forum: Python
    Replies:
    23
    Views:
    859
Loading...

Share This Page