Why can const char* const & = “hello” compile?
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
add a comment |
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
"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
add a comment |
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
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
c++ reference const language-lawyer
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
add a comment |
"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
add a comment |
2 Answers
2
active
oldest
votes
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.
5
Yes I get it know. It's the same reason as forconst 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
|
show 8 more comments
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.
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
5
Yes I get it know. It's the same reason as forconst 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
|
show 8 more comments
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.
5
Yes I get it know. It's the same reason as forconst 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
|
show 8 more comments
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.
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.
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 forconst 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
|
show 8 more comments
5
Yes I get it know. It's the same reason as forconst 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
|
show 8 more comments
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.
add a comment |
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.
add a comment |
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.
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.
edited Sep 18 '18 at 11:13
answered Aug 27 '18 at 4:55
SkepticalEmpiricistSkepticalEmpiricist
4,7731027
4,7731027
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
"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