Value category of conditional operator












4















Consider the following code:



int x;
int& f() {
return x ? x : throw 0;
}


With gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04) I get the following compilation error:



cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’


Note that this compiles just fine in clang. Here is (what I believe to be) the relevant statement from the standard:




N4659 [8.16.2.1] (Conditional Operator):

The second or the third operand (but not both) is a (possibly parenthesized) throw-expression (8.17); the result is of the type and value category of the other.




As far as I understand, x is an lvalue, so it seems to me that clang is right. Am I wrong?




If I had to take a guess, the l-to-rvalue conversion is occurring because the two expressions in the conditional don't have the same type, but because the second is a throw this conversion should be preempted. I'm not familiar with submitting bug reports, but perhaps that would be a better forum for this.


Here are some (probably) more helpful questions about the conditional operator:
Why does this function return an lvalue reference given rvalue arguments?
Error: lvalue required in this simple C code? (Ternary with assignment?)








share|improve this question

























  • It is clearly a bug in g++, the change mentioned by Shafik appears in the standard since C+14

    – M.M
    Nov 16 '18 at 3:47
















4















Consider the following code:



int x;
int& f() {
return x ? x : throw 0;
}


With gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04) I get the following compilation error:



cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’


Note that this compiles just fine in clang. Here is (what I believe to be) the relevant statement from the standard:




N4659 [8.16.2.1] (Conditional Operator):

The second or the third operand (but not both) is a (possibly parenthesized) throw-expression (8.17); the result is of the type and value category of the other.




As far as I understand, x is an lvalue, so it seems to me that clang is right. Am I wrong?




If I had to take a guess, the l-to-rvalue conversion is occurring because the two expressions in the conditional don't have the same type, but because the second is a throw this conversion should be preempted. I'm not familiar with submitting bug reports, but perhaps that would be a better forum for this.


Here are some (probably) more helpful questions about the conditional operator:
Why does this function return an lvalue reference given rvalue arguments?
Error: lvalue required in this simple C code? (Ternary with assignment?)








share|improve this question

























  • It is clearly a bug in g++, the change mentioned by Shafik appears in the standard since C+14

    – M.M
    Nov 16 '18 at 3:47














4












4








4








Consider the following code:



int x;
int& f() {
return x ? x : throw 0;
}


With gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04) I get the following compilation error:



cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’


Note that this compiles just fine in clang. Here is (what I believe to be) the relevant statement from the standard:




N4659 [8.16.2.1] (Conditional Operator):

The second or the third operand (but not both) is a (possibly parenthesized) throw-expression (8.17); the result is of the type and value category of the other.




As far as I understand, x is an lvalue, so it seems to me that clang is right. Am I wrong?




If I had to take a guess, the l-to-rvalue conversion is occurring because the two expressions in the conditional don't have the same type, but because the second is a throw this conversion should be preempted. I'm not familiar with submitting bug reports, but perhaps that would be a better forum for this.


Here are some (probably) more helpful questions about the conditional operator:
Why does this function return an lvalue reference given rvalue arguments?
Error: lvalue required in this simple C code? (Ternary with assignment?)








share|improve this question
















Consider the following code:



int x;
int& f() {
return x ? x : throw 0;
}


With gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04) I get the following compilation error:



cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’


Note that this compiles just fine in clang. Here is (what I believe to be) the relevant statement from the standard:




N4659 [8.16.2.1] (Conditional Operator):

The second or the third operand (but not both) is a (possibly parenthesized) throw-expression (8.17); the result is of the type and value category of the other.




As far as I understand, x is an lvalue, so it seems to me that clang is right. Am I wrong?




If I had to take a guess, the l-to-rvalue conversion is occurring because the two expressions in the conditional don't have the same type, but because the second is a throw this conversion should be preempted. I'm not familiar with submitting bug reports, but perhaps that would be a better forum for this.


Here are some (probably) more helpful questions about the conditional operator:
Why does this function return an lvalue reference given rvalue arguments?
Error: lvalue required in this simple C code? (Ternary with assignment?)





c++ g++ language-lawyer conditional-operator value-categories






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 16 '18 at 1:00









Shafik Yaghmour

127k23327545




127k23327545










asked Nov 15 '18 at 21:52









Nathan ChappellNathan Chappell

1278




1278













  • It is clearly a bug in g++, the change mentioned by Shafik appears in the standard since C+14

    – M.M
    Nov 16 '18 at 3:47



















  • It is clearly a bug in g++, the change mentioned by Shafik appears in the standard since C+14

    – M.M
    Nov 16 '18 at 3:47

















It is clearly a bug in g++, the change mentioned by Shafik appears in the standard since C+14

– M.M
Nov 16 '18 at 3:47





It is clearly a bug in g++, the change mentioned by Shafik appears in the standard since C+14

– M.M
Nov 16 '18 at 3:47












1 Answer
1






active

oldest

votes


















6














clang is correct here, the old behavior was to unconditionally convert the value to a prvalue which it looks like gcc still implements.



This was the subject of DR 1560 which was fixed by the resolution of DR 1550. DR 1560 says:




A glvalue appearing as one operand of a conditional-expression in
which the other operand is a throw-expression is converted to a
prvalue, regardless of how the conditional-expression is used:




If either the second or the third operand has type void, then the lvalue-to-rvalue (7.1 [conv.lval]), array-to-pointer (7.2
[conv.array]), and function-to-pointer (7.3 [conv.func]) standard
conversions are performed on the second and third operands, and one of
the following shall hold:




  • The second or the third operand (but not both) is a throw-expression (18.1 [except.throw]); the result is of the type of
    the other and is a prvalue.




This seems to be gratuitous and surprising.




and DR 1550 changed the wording in [expr.cond] to what we have now:




The second or the third operand (but not both) is a (possibly parenthesized) throw-expression; the result is of the type and value category of the other. The conditional-expression is a bit-field if that operand is a bit-field.




So it looks like gcc is implementing the old behavior while clang is implementing the DR.



This is the patch that applied DR 1560 to clang. It added the following test:



namespace DR1560 { // dr1560: 3.5
void f(bool b, int n) {
(b ? throw 0 : n) = (b ? n : throw 0) = 0;
}
class X { X(const X&); };
const X &get();
const X &x = true ? get() : throw 0;
}


which on godbolt we can see this fails for gcc due to:



error: lvalue required as left operand of assignment
4 | (b ? throw 0 : n) = (b ? n : throw 0) = 0;
|


We have a gcc bug report for a very similar issue which has the following reduced test case:




I wanted to bump this bug and supply a simpler test-case:



void blah(int&) {}

int main() {
int i{};
blah(true ? i : throw);
}


result with gcc 6.0:



prog.cc: In function 'int main()':
prog.cc:6:15: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
blah(true ? i : throw 0);
~~~~~^~~~~~~~~~~~~






share|improve this answer

























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53328407%2fvalue-category-of-conditional-operator%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    6














    clang is correct here, the old behavior was to unconditionally convert the value to a prvalue which it looks like gcc still implements.



    This was the subject of DR 1560 which was fixed by the resolution of DR 1550. DR 1560 says:




    A glvalue appearing as one operand of a conditional-expression in
    which the other operand is a throw-expression is converted to a
    prvalue, regardless of how the conditional-expression is used:




    If either the second or the third operand has type void, then the lvalue-to-rvalue (7.1 [conv.lval]), array-to-pointer (7.2
    [conv.array]), and function-to-pointer (7.3 [conv.func]) standard
    conversions are performed on the second and third operands, and one of
    the following shall hold:




    • The second or the third operand (but not both) is a throw-expression (18.1 [except.throw]); the result is of the type of
      the other and is a prvalue.




    This seems to be gratuitous and surprising.




    and DR 1550 changed the wording in [expr.cond] to what we have now:




    The second or the third operand (but not both) is a (possibly parenthesized) throw-expression; the result is of the type and value category of the other. The conditional-expression is a bit-field if that operand is a bit-field.




    So it looks like gcc is implementing the old behavior while clang is implementing the DR.



    This is the patch that applied DR 1560 to clang. It added the following test:



    namespace DR1560 { // dr1560: 3.5
    void f(bool b, int n) {
    (b ? throw 0 : n) = (b ? n : throw 0) = 0;
    }
    class X { X(const X&); };
    const X &get();
    const X &x = true ? get() : throw 0;
    }


    which on godbolt we can see this fails for gcc due to:



    error: lvalue required as left operand of assignment
    4 | (b ? throw 0 : n) = (b ? n : throw 0) = 0;
    |


    We have a gcc bug report for a very similar issue which has the following reduced test case:




    I wanted to bump this bug and supply a simpler test-case:



    void blah(int&) {}

    int main() {
    int i{};
    blah(true ? i : throw);
    }


    result with gcc 6.0:



    prog.cc: In function 'int main()':
    prog.cc:6:15: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
    blah(true ? i : throw 0);
    ~~~~~^~~~~~~~~~~~~






    share|improve this answer






























      6














      clang is correct here, the old behavior was to unconditionally convert the value to a prvalue which it looks like gcc still implements.



      This was the subject of DR 1560 which was fixed by the resolution of DR 1550. DR 1560 says:




      A glvalue appearing as one operand of a conditional-expression in
      which the other operand is a throw-expression is converted to a
      prvalue, regardless of how the conditional-expression is used:




      If either the second or the third operand has type void, then the lvalue-to-rvalue (7.1 [conv.lval]), array-to-pointer (7.2
      [conv.array]), and function-to-pointer (7.3 [conv.func]) standard
      conversions are performed on the second and third operands, and one of
      the following shall hold:




      • The second or the third operand (but not both) is a throw-expression (18.1 [except.throw]); the result is of the type of
        the other and is a prvalue.




      This seems to be gratuitous and surprising.




      and DR 1550 changed the wording in [expr.cond] to what we have now:




      The second or the third operand (but not both) is a (possibly parenthesized) throw-expression; the result is of the type and value category of the other. The conditional-expression is a bit-field if that operand is a bit-field.




      So it looks like gcc is implementing the old behavior while clang is implementing the DR.



      This is the patch that applied DR 1560 to clang. It added the following test:



      namespace DR1560 { // dr1560: 3.5
      void f(bool b, int n) {
      (b ? throw 0 : n) = (b ? n : throw 0) = 0;
      }
      class X { X(const X&); };
      const X &get();
      const X &x = true ? get() : throw 0;
      }


      which on godbolt we can see this fails for gcc due to:



      error: lvalue required as left operand of assignment
      4 | (b ? throw 0 : n) = (b ? n : throw 0) = 0;
      |


      We have a gcc bug report for a very similar issue which has the following reduced test case:




      I wanted to bump this bug and supply a simpler test-case:



      void blah(int&) {}

      int main() {
      int i{};
      blah(true ? i : throw);
      }


      result with gcc 6.0:



      prog.cc: In function 'int main()':
      prog.cc:6:15: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
      blah(true ? i : throw 0);
      ~~~~~^~~~~~~~~~~~~






      share|improve this answer




























        6












        6








        6







        clang is correct here, the old behavior was to unconditionally convert the value to a prvalue which it looks like gcc still implements.



        This was the subject of DR 1560 which was fixed by the resolution of DR 1550. DR 1560 says:




        A glvalue appearing as one operand of a conditional-expression in
        which the other operand is a throw-expression is converted to a
        prvalue, regardless of how the conditional-expression is used:




        If either the second or the third operand has type void, then the lvalue-to-rvalue (7.1 [conv.lval]), array-to-pointer (7.2
        [conv.array]), and function-to-pointer (7.3 [conv.func]) standard
        conversions are performed on the second and third operands, and one of
        the following shall hold:




        • The second or the third operand (but not both) is a throw-expression (18.1 [except.throw]); the result is of the type of
          the other and is a prvalue.




        This seems to be gratuitous and surprising.




        and DR 1550 changed the wording in [expr.cond] to what we have now:




        The second or the third operand (but not both) is a (possibly parenthesized) throw-expression; the result is of the type and value category of the other. The conditional-expression is a bit-field if that operand is a bit-field.




        So it looks like gcc is implementing the old behavior while clang is implementing the DR.



        This is the patch that applied DR 1560 to clang. It added the following test:



        namespace DR1560 { // dr1560: 3.5
        void f(bool b, int n) {
        (b ? throw 0 : n) = (b ? n : throw 0) = 0;
        }
        class X { X(const X&); };
        const X &get();
        const X &x = true ? get() : throw 0;
        }


        which on godbolt we can see this fails for gcc due to:



        error: lvalue required as left operand of assignment
        4 | (b ? throw 0 : n) = (b ? n : throw 0) = 0;
        |


        We have a gcc bug report for a very similar issue which has the following reduced test case:




        I wanted to bump this bug and supply a simpler test-case:



        void blah(int&) {}

        int main() {
        int i{};
        blah(true ? i : throw);
        }


        result with gcc 6.0:



        prog.cc: In function 'int main()':
        prog.cc:6:15: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
        blah(true ? i : throw 0);
        ~~~~~^~~~~~~~~~~~~






        share|improve this answer















        clang is correct here, the old behavior was to unconditionally convert the value to a prvalue which it looks like gcc still implements.



        This was the subject of DR 1560 which was fixed by the resolution of DR 1550. DR 1560 says:




        A glvalue appearing as one operand of a conditional-expression in
        which the other operand is a throw-expression is converted to a
        prvalue, regardless of how the conditional-expression is used:




        If either the second or the third operand has type void, then the lvalue-to-rvalue (7.1 [conv.lval]), array-to-pointer (7.2
        [conv.array]), and function-to-pointer (7.3 [conv.func]) standard
        conversions are performed on the second and third operands, and one of
        the following shall hold:




        • The second or the third operand (but not both) is a throw-expression (18.1 [except.throw]); the result is of the type of
          the other and is a prvalue.




        This seems to be gratuitous and surprising.




        and DR 1550 changed the wording in [expr.cond] to what we have now:




        The second or the third operand (but not both) is a (possibly parenthesized) throw-expression; the result is of the type and value category of the other. The conditional-expression is a bit-field if that operand is a bit-field.




        So it looks like gcc is implementing the old behavior while clang is implementing the DR.



        This is the patch that applied DR 1560 to clang. It added the following test:



        namespace DR1560 { // dr1560: 3.5
        void f(bool b, int n) {
        (b ? throw 0 : n) = (b ? n : throw 0) = 0;
        }
        class X { X(const X&); };
        const X &get();
        const X &x = true ? get() : throw 0;
        }


        which on godbolt we can see this fails for gcc due to:



        error: lvalue required as left operand of assignment
        4 | (b ? throw 0 : n) = (b ? n : throw 0) = 0;
        |


        We have a gcc bug report for a very similar issue which has the following reduced test case:




        I wanted to bump this bug and supply a simpler test-case:



        void blah(int&) {}

        int main() {
        int i{};
        blah(true ? i : throw);
        }


        result with gcc 6.0:



        prog.cc: In function 'int main()':
        prog.cc:6:15: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
        blah(true ? i : throw 0);
        ~~~~~^~~~~~~~~~~~~







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Dec 30 '18 at 2:58

























        answered Nov 15 '18 at 21:59









        Shafik YaghmourShafik Yaghmour

        127k23327545




        127k23327545
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53328407%2fvalue-category-of-conditional-operator%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Xamarin.iOS Cant Deploy on Iphone

            Glorious Revolution

            Dulmage-Mendelsohn matrix decomposition in Python