3
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?

xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • 2
    `T&&` is not const in that context (https://wandbox.org/permlink/EheQgijGM7Mxtuw6) – Matthieu Brucher Feb 01 '19 at 13:17
  • Also note that you are not modifiying a `const` object. `n` is not `const`. You could let `f` take a `const& int` and via a `const_cast` modify `n`, weird but nothing illegal, just saying... – 463035818_is_not_an_ai Feb 01 '19 at 13:23
  • 2
    Writing `const` in front is a confusing habit, things are much clearer when you write `T const&`, which is then `int&const&`, where it is obvious that you are trying to apply const to the reference instead of applying it to int. – Marc Glisse Feb 01 '19 at 13:34

1 Answers1

6

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.

zedu
  • 135
  • 4