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 as i: when i is a int const, why ri is not const?

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.