Why are references not "const" in C++?
We know that a "const variable" indicates that once assigned, you cannot change the variable, like this:
int const i = 1;
i = 2;
The program above will fail to compile; gcc prompts with an error:
assignment of read-only variable 'i'
No problem, I can understand it, but the following example is beyond my understanding:
#include<iostream>
using namespace std;
int main()
{
boolalpha(cout);
int const i = 1;
cout << is_const<decltype(i)>::value << endl;
int const &ri = i;
cout << is_const<decltype(ri)>::value << endl;
return 0;
}
It outputs
true
false
Weird. We know that once a reference is bound to a name/variable, we cannot change this binding, we change its bound object. So I suppose the type of ri
should be the same as i
: when i
is an int const
, why is ri
not const
?
This may seem counter-intuitive but I think the way to understand this is to realize that, in certain respects, references are treated syntactically like pointers.
This seems logical for a pointer:
int main()
{
boolalpha(cout);
int const i = 1;
cout << is_const<decltype(i)>::value << endl;
int const* ri = &i;
cout << is_const<decltype(ri)>::value << endl;
}
Output:
true
false
This is logical because we know it is not the pointer object that is const (it can be made to point elsewhere) it is the object that is being pointed to.
So we correctly see the constness of the pointer itself returned as false
.
If we want to make the pointer itself const
we have to say:
int main()
{
boolalpha(cout);
int const i = 1;
cout << is_const<decltype(i)>::value << endl;
int const* const ri = &i;
cout << is_const<decltype(ri)>::value << endl;
}
Output:
true
true
And so I think we see a syntactic analogy with the reference.
However references are semantically different to pointers especially in one crucial respect, we are not allowed to rebind a reference to another object once bound.
So even though references share the same syntax as pointers the rules are different and so the language prevents us from declaring the reference itself const
like this:
int main()
{
boolalpha(cout);
int const i = 1;
cout << is_const<decltype(i)>::value << endl;
int const& const ri = i; // COMPILE TIME ERROR!
cout << is_const<decltype(ri)>::value << endl;
}
I assume we are not allowed to do this because it doesn't appear to be needed when the language rules prevent the reference from being rebound in the same way a pointer could(if it is not declared const
).
So to answer the question:
Q) Why “reference” is not a “const” in C++?
In your example the syntax makes the thing being referred to const
the same way it would if you were declaring a pointer.
Rightly or wrongly we are not allowed to make the reference itself const
but if we were it would look like this:
int const& const ri = i; // not allowed
Q) we know once a reference is bind to a name/variable, we cannot change this binding, we change its binded object. So I suppose the type of
ri
should be same asi
: wheni
is aint const
, whyri
is notconst
?
Why is the decltype()
not transferred to the object the referece is bound to?
I suppose this is for semantic equivalence with pointers and maybe also the function of decltype()
(declared type) is to look back at what was declared before the binding took place.
why is "ri" not "const"?
std::is_const
checks whether the type is const-qualified or not.
If T is a const-qualified type (that is, const, or const volatile), provides the member constant value equal true. For any other type, value is false.
But the reference can't be const-qualified. References [dcl.ref]/1
Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef-name ([dcl.typedef], [temp.param]) or decltype-specifier ([dcl.type.simple]), in which case the cv-qualifiers are ignored.
So is_const<decltype(ri)>::value
will return false
becuase ri
(the reference) is not a const-qualified type. As you said, we can't rebind a reference after initialization, which implies reference is always "const", on the other hand, const-qualified reference or const-unqualified reference might not make sense actually.
You need to use std::remove_reference
for get the value you're looking for.
std::cout << std::is_const<std::remove_reference<decltype(ri)>::type>::value << std::endl;
For more information, see this post.