template<typename T>
void f(T&& n)
{
++n; // ok to modify a const object, why?
}
template<typename T>
void g()
{
int n{};
f<const T&>(n);
}
int main()
{
g<int&>();
}
As shown in the code above. My question is:
Why does universal reference not keep constness of its arguments?
It does. In your example, you are attempting to apply const to the reference type itself, not to int. You can see it cleaner if you write const after the type:
const T& == T const& == int& const&.
As const doesn't change anything when applied to reference types, it is simply ignored.
In fact, if you wrote int& const
without templates, you would get a compilation error. However it is allowed in templates, because in some cases it is hard to avoid such types.
Another way of looking at this would be replacing references with pointers. If you did so, you would get the following type:
const T* = T const* = int* const*
This type is a pointer to an immutable (const) pointer to a mutable (non-const) int.