Why can const char* const & = “hello” compile?










52















I am reading a code snippet from a book and find this:



const char* const & a = "hello"; //can compile 
const char*& a = "hello"; //cannot


All I know is that when initializing a reference, the array to pointer conversion would not take place.



const char* const &, a reference to a const pointer, the pointer points to const char.



const char*&, a reference to a pointer, the pointer points to
const char.



So why does adding an extra const, indicating that the pointer is a const, allow it to compile?










share|improve this question
























  • "when initializing a reference, the array to pointer conversion would not take place". This is not exactly correct. When initialising a reference to an array, the array to pointer conversion would not take place. There is no such rule for initialising a reference to a pointer.

    – n.m.
    Sep 18 '18 at 5:51















52















I am reading a code snippet from a book and find this:



const char* const & a = "hello"; //can compile 
const char*& a = "hello"; //cannot


All I know is that when initializing a reference, the array to pointer conversion would not take place.



const char* const &, a reference to a const pointer, the pointer points to const char.



const char*&, a reference to a pointer, the pointer points to
const char.



So why does adding an extra const, indicating that the pointer is a const, allow it to compile?










share|improve this question
























  • "when initializing a reference, the array to pointer conversion would not take place". This is not exactly correct. When initialising a reference to an array, the array to pointer conversion would not take place. There is no such rule for initialising a reference to a pointer.

    – n.m.
    Sep 18 '18 at 5:51













52












52








52


7






I am reading a code snippet from a book and find this:



const char* const & a = "hello"; //can compile 
const char*& a = "hello"; //cannot


All I know is that when initializing a reference, the array to pointer conversion would not take place.



const char* const &, a reference to a const pointer, the pointer points to const char.



const char*&, a reference to a pointer, the pointer points to
const char.



So why does adding an extra const, indicating that the pointer is a const, allow it to compile?










share|improve this question
















I am reading a code snippet from a book and find this:



const char* const & a = "hello"; //can compile 
const char*& a = "hello"; //cannot


All I know is that when initializing a reference, the array to pointer conversion would not take place.



const char* const &, a reference to a const pointer, the pointer points to const char.



const char*&, a reference to a pointer, the pointer points to
const char.



So why does adding an extra const, indicating that the pointer is a const, allow it to compile?







c++ reference const language-lawyer






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Aug 27 '18 at 15:23









Boann

37.1k1290121




37.1k1290121










asked Aug 27 '18 at 4:39









RickRick

1,573929




1,573929












  • "when initializing a reference, the array to pointer conversion would not take place". This is not exactly correct. When initialising a reference to an array, the array to pointer conversion would not take place. There is no such rule for initialising a reference to a pointer.

    – n.m.
    Sep 18 '18 at 5:51

















  • "when initializing a reference, the array to pointer conversion would not take place". This is not exactly correct. When initialising a reference to an array, the array to pointer conversion would not take place. There is no such rule for initialising a reference to a pointer.

    – n.m.
    Sep 18 '18 at 5:51
















"when initializing a reference, the array to pointer conversion would not take place". This is not exactly correct. When initialising a reference to an array, the array to pointer conversion would not take place. There is no such rule for initialising a reference to a pointer.

– n.m.
Sep 18 '18 at 5:51





"when initializing a reference, the array to pointer conversion would not take place". This is not exactly correct. When initialising a reference to an array, the array to pointer conversion would not take place. There is no such rule for initialising a reference to a pointer.

– n.m.
Sep 18 '18 at 5:51












2 Answers
2






active

oldest

votes


















37














It's essentially adhering to this formula



T const & a = something_convertible_to_T;


Where T is const char*. In the first case, a temporary pointer can be materialized, assigned the address of the literal, and then have itself bound to the reference. In the second case, since the lvalue reference isn't const, it can't happen. Another example of more of the same



const char* && a = "hello"; // rvalue ref makes a no into a yes.


Now the temporary pointer is bound to an rvalue reference.






share|improve this answer




















  • 5





    Yes I get it know. It's the same reason as for const int& a = 3; //yes, int&a = 3; //no. The formula is very helpful. Thanks. It merely becomes harder to recognize that when comes to pointer.

    – Rick
    Aug 27 '18 at 5:03












  • @SkepticalEmpiricist - It is. What's important to consider when taking in that comment is that the identity conversion also counts (though it's obviously not a very interesting case). const int& a = 3; materializes a temporary from a literal int prvalue, and binds a reference to it.

    – StoryTeller
    Aug 27 '18 at 7:34











  • @StoryTeller I still cannot fully follow, I thought I know what the identity conversion means in The Standard but I cannot put it into use trying to understand what you're saying. Can I kindly ask you to elaborate on this?

    – SkepticalEmpiricist
    Aug 27 '18 at 7:44






  • 1





    @SkepticalEmpiricist - I wasn't trying to be as exact as the standard is on this, but my line of thought (and explanation) tries to follow the C++17 changes to prvalues, the temporary materialization conversion and this bit about initializing references.

    – StoryTeller
    Aug 27 '18 at 7:50












  • @SkepticalEmpiricist - It's simpler than that. The temporary can be materialized in all the examples. It's just that the whole of [dcl.init.ref]/5 doesn't define the meaning of such an initialization. So the program is ill-formed.

    – StoryTeller
    Aug 27 '18 at 8:17


















8














Some additional phrasing for the bored, after reading the superb answer by @StoryTeller, as I had to go through a different thought process about this.



So syntactically, In both lines we define a reference a, and in both we shall have a materialization of a temporary pointer that takes the address of the string literal. The only difference between the two is the 2nd const appearing only here:



const char* const & a = "hello";


and not here:



const char*& a = "hello";


This 2nd const denotes that the object being referenced here, a pointer in this case, is itself const, as in it cannot be modified using this reference.



Hence, because the type of this string literal is const char[6] (and not const char * for example), our lvalue reference to type const char* in the second line cannot bind to it -- but the reference in the first line, being a reference to type const char* const could. Why? Because of the rules of reference initialization:




(5) A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:




  • (5.1) If the reference is an lvalue reference and the initializer expression



    • (5.1.1) is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2”, [...]

    • (5.1.2) has a class type (i.e., T2 is a class type) [...]




Both the expressions are lvalues, but our “cv1 T1” is not reference-compatible with our “cv2 T2” and “T2” is not a class type.




  • (5.2) Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed



The reference is indeed not const-qualified: our “T1” is const char*, which is a pointer to const , as opposed to a const pointer. The actual type here is a pointer type, so this is what matters.



The Clang error for the second line, read with that in mind, tells us exactly this:



error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type 'const char [6]'
const char*& a = "hello";
^ ~~~~~~~


The part about being non-const lvalue reference is exactly ¶5.2 -- the lvalue in our case is a pointer to const, but itself isn't const! Unlike the first line. The part about binding to an unrelated type is exactly ¶5.1 -- our const char* isn't compatible with the RHS being const char[6], or const char* const after array to pointer conversion.



For this exact reason, or lack thereof, this can compiles error-free:



char* const & a = "hello";


ISO C++11 warning aside, a compiler lets this one pass (not that it should, as the string literal is a 'const char 6' and we shouldn't be dropping this first const), as the reference is now const with regards to its object, the pointer.



Another interesting thing is that An rvalue reference const char* && a (no "2nd const") could bind to the temporary pointer that has materialized from the string literal, as @StoryTeller provided himself. Why is that? Because of the rules of array to pointer conversion:




An lvalue or rvalue of type "array of N T" or "array of unknown bound
of T" can be converted to a prvalue of type "pointer to T".




No mention of const or other cv-qualification phrasing in here, but this stands only as long as we're initializing an rvalue ref.






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%2f52032645%2fwhy-can-const-char-const-hello-compile%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    37














    It's essentially adhering to this formula



    T const & a = something_convertible_to_T;


    Where T is const char*. In the first case, a temporary pointer can be materialized, assigned the address of the literal, and then have itself bound to the reference. In the second case, since the lvalue reference isn't const, it can't happen. Another example of more of the same



    const char* && a = "hello"; // rvalue ref makes a no into a yes.


    Now the temporary pointer is bound to an rvalue reference.






    share|improve this answer




















    • 5





      Yes I get it know. It's the same reason as for const int& a = 3; //yes, int&a = 3; //no. The formula is very helpful. Thanks. It merely becomes harder to recognize that when comes to pointer.

      – Rick
      Aug 27 '18 at 5:03












    • @SkepticalEmpiricist - It is. What's important to consider when taking in that comment is that the identity conversion also counts (though it's obviously not a very interesting case). const int& a = 3; materializes a temporary from a literal int prvalue, and binds a reference to it.

      – StoryTeller
      Aug 27 '18 at 7:34











    • @StoryTeller I still cannot fully follow, I thought I know what the identity conversion means in The Standard but I cannot put it into use trying to understand what you're saying. Can I kindly ask you to elaborate on this?

      – SkepticalEmpiricist
      Aug 27 '18 at 7:44






    • 1





      @SkepticalEmpiricist - I wasn't trying to be as exact as the standard is on this, but my line of thought (and explanation) tries to follow the C++17 changes to prvalues, the temporary materialization conversion and this bit about initializing references.

      – StoryTeller
      Aug 27 '18 at 7:50












    • @SkepticalEmpiricist - It's simpler than that. The temporary can be materialized in all the examples. It's just that the whole of [dcl.init.ref]/5 doesn't define the meaning of such an initialization. So the program is ill-formed.

      – StoryTeller
      Aug 27 '18 at 8:17















    37














    It's essentially adhering to this formula



    T const & a = something_convertible_to_T;


    Where T is const char*. In the first case, a temporary pointer can be materialized, assigned the address of the literal, and then have itself bound to the reference. In the second case, since the lvalue reference isn't const, it can't happen. Another example of more of the same



    const char* && a = "hello"; // rvalue ref makes a no into a yes.


    Now the temporary pointer is bound to an rvalue reference.






    share|improve this answer




















    • 5





      Yes I get it know. It's the same reason as for const int& a = 3; //yes, int&a = 3; //no. The formula is very helpful. Thanks. It merely becomes harder to recognize that when comes to pointer.

      – Rick
      Aug 27 '18 at 5:03












    • @SkepticalEmpiricist - It is. What's important to consider when taking in that comment is that the identity conversion also counts (though it's obviously not a very interesting case). const int& a = 3; materializes a temporary from a literal int prvalue, and binds a reference to it.

      – StoryTeller
      Aug 27 '18 at 7:34











    • @StoryTeller I still cannot fully follow, I thought I know what the identity conversion means in The Standard but I cannot put it into use trying to understand what you're saying. Can I kindly ask you to elaborate on this?

      – SkepticalEmpiricist
      Aug 27 '18 at 7:44






    • 1





      @SkepticalEmpiricist - I wasn't trying to be as exact as the standard is on this, but my line of thought (and explanation) tries to follow the C++17 changes to prvalues, the temporary materialization conversion and this bit about initializing references.

      – StoryTeller
      Aug 27 '18 at 7:50












    • @SkepticalEmpiricist - It's simpler than that. The temporary can be materialized in all the examples. It's just that the whole of [dcl.init.ref]/5 doesn't define the meaning of such an initialization. So the program is ill-formed.

      – StoryTeller
      Aug 27 '18 at 8:17













    37












    37








    37







    It's essentially adhering to this formula



    T const & a = something_convertible_to_T;


    Where T is const char*. In the first case, a temporary pointer can be materialized, assigned the address of the literal, and then have itself bound to the reference. In the second case, since the lvalue reference isn't const, it can't happen. Another example of more of the same



    const char* && a = "hello"; // rvalue ref makes a no into a yes.


    Now the temporary pointer is bound to an rvalue reference.






    share|improve this answer















    It's essentially adhering to this formula



    T const & a = something_convertible_to_T;


    Where T is const char*. In the first case, a temporary pointer can be materialized, assigned the address of the literal, and then have itself bound to the reference. In the second case, since the lvalue reference isn't const, it can't happen. Another example of more of the same



    const char* && a = "hello"; // rvalue ref makes a no into a yes.


    Now the temporary pointer is bound to an rvalue reference.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Aug 27 '18 at 5:03

























    answered Aug 27 '18 at 4:42









    StoryTellerStoryTeller

    99.4k12201271




    99.4k12201271







    • 5





      Yes I get it know. It's the same reason as for const int& a = 3; //yes, int&a = 3; //no. The formula is very helpful. Thanks. It merely becomes harder to recognize that when comes to pointer.

      – Rick
      Aug 27 '18 at 5:03












    • @SkepticalEmpiricist - It is. What's important to consider when taking in that comment is that the identity conversion also counts (though it's obviously not a very interesting case). const int& a = 3; materializes a temporary from a literal int prvalue, and binds a reference to it.

      – StoryTeller
      Aug 27 '18 at 7:34











    • @StoryTeller I still cannot fully follow, I thought I know what the identity conversion means in The Standard but I cannot put it into use trying to understand what you're saying. Can I kindly ask you to elaborate on this?

      – SkepticalEmpiricist
      Aug 27 '18 at 7:44






    • 1





      @SkepticalEmpiricist - I wasn't trying to be as exact as the standard is on this, but my line of thought (and explanation) tries to follow the C++17 changes to prvalues, the temporary materialization conversion and this bit about initializing references.

      – StoryTeller
      Aug 27 '18 at 7:50












    • @SkepticalEmpiricist - It's simpler than that. The temporary can be materialized in all the examples. It's just that the whole of [dcl.init.ref]/5 doesn't define the meaning of such an initialization. So the program is ill-formed.

      – StoryTeller
      Aug 27 '18 at 8:17












    • 5





      Yes I get it know. It's the same reason as for const int& a = 3; //yes, int&a = 3; //no. The formula is very helpful. Thanks. It merely becomes harder to recognize that when comes to pointer.

      – Rick
      Aug 27 '18 at 5:03












    • @SkepticalEmpiricist - It is. What's important to consider when taking in that comment is that the identity conversion also counts (though it's obviously not a very interesting case). const int& a = 3; materializes a temporary from a literal int prvalue, and binds a reference to it.

      – StoryTeller
      Aug 27 '18 at 7:34











    • @StoryTeller I still cannot fully follow, I thought I know what the identity conversion means in The Standard but I cannot put it into use trying to understand what you're saying. Can I kindly ask you to elaborate on this?

      – SkepticalEmpiricist
      Aug 27 '18 at 7:44






    • 1





      @SkepticalEmpiricist - I wasn't trying to be as exact as the standard is on this, but my line of thought (and explanation) tries to follow the C++17 changes to prvalues, the temporary materialization conversion and this bit about initializing references.

      – StoryTeller
      Aug 27 '18 at 7:50












    • @SkepticalEmpiricist - It's simpler than that. The temporary can be materialized in all the examples. It's just that the whole of [dcl.init.ref]/5 doesn't define the meaning of such an initialization. So the program is ill-formed.

      – StoryTeller
      Aug 27 '18 at 8:17







    5




    5





    Yes I get it know. It's the same reason as for const int& a = 3; //yes, int&a = 3; //no. The formula is very helpful. Thanks. It merely becomes harder to recognize that when comes to pointer.

    – Rick
    Aug 27 '18 at 5:03






    Yes I get it know. It's the same reason as for const int& a = 3; //yes, int&a = 3; //no. The formula is very helpful. Thanks. It merely becomes harder to recognize that when comes to pointer.

    – Rick
    Aug 27 '18 at 5:03














    @SkepticalEmpiricist - It is. What's important to consider when taking in that comment is that the identity conversion also counts (though it's obviously not a very interesting case). const int& a = 3; materializes a temporary from a literal int prvalue, and binds a reference to it.

    – StoryTeller
    Aug 27 '18 at 7:34





    @SkepticalEmpiricist - It is. What's important to consider when taking in that comment is that the identity conversion also counts (though it's obviously not a very interesting case). const int& a = 3; materializes a temporary from a literal int prvalue, and binds a reference to it.

    – StoryTeller
    Aug 27 '18 at 7:34













    @StoryTeller I still cannot fully follow, I thought I know what the identity conversion means in The Standard but I cannot put it into use trying to understand what you're saying. Can I kindly ask you to elaborate on this?

    – SkepticalEmpiricist
    Aug 27 '18 at 7:44





    @StoryTeller I still cannot fully follow, I thought I know what the identity conversion means in The Standard but I cannot put it into use trying to understand what you're saying. Can I kindly ask you to elaborate on this?

    – SkepticalEmpiricist
    Aug 27 '18 at 7:44




    1




    1





    @SkepticalEmpiricist - I wasn't trying to be as exact as the standard is on this, but my line of thought (and explanation) tries to follow the C++17 changes to prvalues, the temporary materialization conversion and this bit about initializing references.

    – StoryTeller
    Aug 27 '18 at 7:50






    @SkepticalEmpiricist - I wasn't trying to be as exact as the standard is on this, but my line of thought (and explanation) tries to follow the C++17 changes to prvalues, the temporary materialization conversion and this bit about initializing references.

    – StoryTeller
    Aug 27 '18 at 7:50














    @SkepticalEmpiricist - It's simpler than that. The temporary can be materialized in all the examples. It's just that the whole of [dcl.init.ref]/5 doesn't define the meaning of such an initialization. So the program is ill-formed.

    – StoryTeller
    Aug 27 '18 at 8:17





    @SkepticalEmpiricist - It's simpler than that. The temporary can be materialized in all the examples. It's just that the whole of [dcl.init.ref]/5 doesn't define the meaning of such an initialization. So the program is ill-formed.

    – StoryTeller
    Aug 27 '18 at 8:17













    8














    Some additional phrasing for the bored, after reading the superb answer by @StoryTeller, as I had to go through a different thought process about this.



    So syntactically, In both lines we define a reference a, and in both we shall have a materialization of a temporary pointer that takes the address of the string literal. The only difference between the two is the 2nd const appearing only here:



    const char* const & a = "hello";


    and not here:



    const char*& a = "hello";


    This 2nd const denotes that the object being referenced here, a pointer in this case, is itself const, as in it cannot be modified using this reference.



    Hence, because the type of this string literal is const char[6] (and not const char * for example), our lvalue reference to type const char* in the second line cannot bind to it -- but the reference in the first line, being a reference to type const char* const could. Why? Because of the rules of reference initialization:




    (5) A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:




    • (5.1) If the reference is an lvalue reference and the initializer expression



      • (5.1.1) is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2”, [...]

      • (5.1.2) has a class type (i.e., T2 is a class type) [...]




    Both the expressions are lvalues, but our “cv1 T1” is not reference-compatible with our “cv2 T2” and “T2” is not a class type.




    • (5.2) Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed



    The reference is indeed not const-qualified: our “T1” is const char*, which is a pointer to const , as opposed to a const pointer. The actual type here is a pointer type, so this is what matters.



    The Clang error for the second line, read with that in mind, tells us exactly this:



    error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type 'const char [6]'
    const char*& a = "hello";
    ^ ~~~~~~~


    The part about being non-const lvalue reference is exactly ¶5.2 -- the lvalue in our case is a pointer to const, but itself isn't const! Unlike the first line. The part about binding to an unrelated type is exactly ¶5.1 -- our const char* isn't compatible with the RHS being const char[6], or const char* const after array to pointer conversion.



    For this exact reason, or lack thereof, this can compiles error-free:



    char* const & a = "hello";


    ISO C++11 warning aside, a compiler lets this one pass (not that it should, as the string literal is a 'const char 6' and we shouldn't be dropping this first const), as the reference is now const with regards to its object, the pointer.



    Another interesting thing is that An rvalue reference const char* && a (no "2nd const") could bind to the temporary pointer that has materialized from the string literal, as @StoryTeller provided himself. Why is that? Because of the rules of array to pointer conversion:




    An lvalue or rvalue of type "array of N T" or "array of unknown bound
    of T" can be converted to a prvalue of type "pointer to T".




    No mention of const or other cv-qualification phrasing in here, but this stands only as long as we're initializing an rvalue ref.






    share|improve this answer





























      8














      Some additional phrasing for the bored, after reading the superb answer by @StoryTeller, as I had to go through a different thought process about this.



      So syntactically, In both lines we define a reference a, and in both we shall have a materialization of a temporary pointer that takes the address of the string literal. The only difference between the two is the 2nd const appearing only here:



      const char* const & a = "hello";


      and not here:



      const char*& a = "hello";


      This 2nd const denotes that the object being referenced here, a pointer in this case, is itself const, as in it cannot be modified using this reference.



      Hence, because the type of this string literal is const char[6] (and not const char * for example), our lvalue reference to type const char* in the second line cannot bind to it -- but the reference in the first line, being a reference to type const char* const could. Why? Because of the rules of reference initialization:




      (5) A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:




      • (5.1) If the reference is an lvalue reference and the initializer expression



        • (5.1.1) is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2”, [...]

        • (5.1.2) has a class type (i.e., T2 is a class type) [...]




      Both the expressions are lvalues, but our “cv1 T1” is not reference-compatible with our “cv2 T2” and “T2” is not a class type.




      • (5.2) Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed



      The reference is indeed not const-qualified: our “T1” is const char*, which is a pointer to const , as opposed to a const pointer. The actual type here is a pointer type, so this is what matters.



      The Clang error for the second line, read with that in mind, tells us exactly this:



      error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type 'const char [6]'
      const char*& a = "hello";
      ^ ~~~~~~~


      The part about being non-const lvalue reference is exactly ¶5.2 -- the lvalue in our case is a pointer to const, but itself isn't const! Unlike the first line. The part about binding to an unrelated type is exactly ¶5.1 -- our const char* isn't compatible with the RHS being const char[6], or const char* const after array to pointer conversion.



      For this exact reason, or lack thereof, this can compiles error-free:



      char* const & a = "hello";


      ISO C++11 warning aside, a compiler lets this one pass (not that it should, as the string literal is a 'const char 6' and we shouldn't be dropping this first const), as the reference is now const with regards to its object, the pointer.



      Another interesting thing is that An rvalue reference const char* && a (no "2nd const") could bind to the temporary pointer that has materialized from the string literal, as @StoryTeller provided himself. Why is that? Because of the rules of array to pointer conversion:




      An lvalue or rvalue of type "array of N T" or "array of unknown bound
      of T" can be converted to a prvalue of type "pointer to T".




      No mention of const or other cv-qualification phrasing in here, but this stands only as long as we're initializing an rvalue ref.






      share|improve this answer



























        8












        8








        8







        Some additional phrasing for the bored, after reading the superb answer by @StoryTeller, as I had to go through a different thought process about this.



        So syntactically, In both lines we define a reference a, and in both we shall have a materialization of a temporary pointer that takes the address of the string literal. The only difference between the two is the 2nd const appearing only here:



        const char* const & a = "hello";


        and not here:



        const char*& a = "hello";


        This 2nd const denotes that the object being referenced here, a pointer in this case, is itself const, as in it cannot be modified using this reference.



        Hence, because the type of this string literal is const char[6] (and not const char * for example), our lvalue reference to type const char* in the second line cannot bind to it -- but the reference in the first line, being a reference to type const char* const could. Why? Because of the rules of reference initialization:




        (5) A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:




        • (5.1) If the reference is an lvalue reference and the initializer expression



          • (5.1.1) is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2”, [...]

          • (5.1.2) has a class type (i.e., T2 is a class type) [...]




        Both the expressions are lvalues, but our “cv1 T1” is not reference-compatible with our “cv2 T2” and “T2” is not a class type.




        • (5.2) Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed



        The reference is indeed not const-qualified: our “T1” is const char*, which is a pointer to const , as opposed to a const pointer. The actual type here is a pointer type, so this is what matters.



        The Clang error for the second line, read with that in mind, tells us exactly this:



        error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type 'const char [6]'
        const char*& a = "hello";
        ^ ~~~~~~~


        The part about being non-const lvalue reference is exactly ¶5.2 -- the lvalue in our case is a pointer to const, but itself isn't const! Unlike the first line. The part about binding to an unrelated type is exactly ¶5.1 -- our const char* isn't compatible with the RHS being const char[6], or const char* const after array to pointer conversion.



        For this exact reason, or lack thereof, this can compiles error-free:



        char* const & a = "hello";


        ISO C++11 warning aside, a compiler lets this one pass (not that it should, as the string literal is a 'const char 6' and we shouldn't be dropping this first const), as the reference is now const with regards to its object, the pointer.



        Another interesting thing is that An rvalue reference const char* && a (no "2nd const") could bind to the temporary pointer that has materialized from the string literal, as @StoryTeller provided himself. Why is that? Because of the rules of array to pointer conversion:




        An lvalue or rvalue of type "array of N T" or "array of unknown bound
        of T" can be converted to a prvalue of type "pointer to T".




        No mention of const or other cv-qualification phrasing in here, but this stands only as long as we're initializing an rvalue ref.






        share|improve this answer















        Some additional phrasing for the bored, after reading the superb answer by @StoryTeller, as I had to go through a different thought process about this.



        So syntactically, In both lines we define a reference a, and in both we shall have a materialization of a temporary pointer that takes the address of the string literal. The only difference between the two is the 2nd const appearing only here:



        const char* const & a = "hello";


        and not here:



        const char*& a = "hello";


        This 2nd const denotes that the object being referenced here, a pointer in this case, is itself const, as in it cannot be modified using this reference.



        Hence, because the type of this string literal is const char[6] (and not const char * for example), our lvalue reference to type const char* in the second line cannot bind to it -- but the reference in the first line, being a reference to type const char* const could. Why? Because of the rules of reference initialization:




        (5) A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:




        • (5.1) If the reference is an lvalue reference and the initializer expression



          • (5.1.1) is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2”, [...]

          • (5.1.2) has a class type (i.e., T2 is a class type) [...]




        Both the expressions are lvalues, but our “cv1 T1” is not reference-compatible with our “cv2 T2” and “T2” is not a class type.




        • (5.2) Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed



        The reference is indeed not const-qualified: our “T1” is const char*, which is a pointer to const , as opposed to a const pointer. The actual type here is a pointer type, so this is what matters.



        The Clang error for the second line, read with that in mind, tells us exactly this:



        error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type 'const char [6]'
        const char*& a = "hello";
        ^ ~~~~~~~


        The part about being non-const lvalue reference is exactly ¶5.2 -- the lvalue in our case is a pointer to const, but itself isn't const! Unlike the first line. The part about binding to an unrelated type is exactly ¶5.1 -- our const char* isn't compatible with the RHS being const char[6], or const char* const after array to pointer conversion.



        For this exact reason, or lack thereof, this can compiles error-free:



        char* const & a = "hello";


        ISO C++11 warning aside, a compiler lets this one pass (not that it should, as the string literal is a 'const char 6' and we shouldn't be dropping this first const), as the reference is now const with regards to its object, the pointer.



        Another interesting thing is that An rvalue reference const char* && a (no "2nd const") could bind to the temporary pointer that has materialized from the string literal, as @StoryTeller provided himself. Why is that? Because of the rules of array to pointer conversion:




        An lvalue or rvalue of type "array of N T" or "array of unknown bound
        of T" can be converted to a prvalue of type "pointer to T".




        No mention of const or other cv-qualification phrasing in here, but this stands only as long as we're initializing an rvalue ref.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Sep 18 '18 at 11:13

























        answered Aug 27 '18 at 4:55









        SkepticalEmpiricistSkepticalEmpiricist

        4,7731027




        4,7731027



























            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%2f52032645%2fwhy-can-const-char-const-hello-compile%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

            𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

            Edmonton

            Crossroads (UK TV series)