-->

Why does the const in a const T& parameter disappe

2020-04-06 10:52发布

问题:

This question already has answers here:
Closed 5 years ago.

The following code shows that if a template taking a ref-to-const parameter is instantiated with a reference type (e.g., int&), the parameter isn't const:

#include <iostream>

template<typename T>
void f(const T& arg)         // arg isn't const if T is a reference type
{
  arg = -1;
}

int main()
{
  int x = 0;
  f<int&>(x);                // instantiate f with reference type
  std::cout << x << '\n';    // prints -1 under gcc, clang, and msvc
}

What's going on here?

My guess is that the initial type of arg is int & const & and that this somehow transforms to int&. If that's so, exactly how does that happen, in terms of the standard? If that's not what's going on, what is?

回答1:

Thanks to Vlad from Moscow's answer to C++: template function with explicitly specified reference type as type parameter, I believe the crux of the const-disappearance is 8.3.2/1, which says:

In a declaration T D where D has either of the forms
& attribute-specifier-seqoptD1
&& attribute-specifier-seqoptD1
and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T,” then the type of the identifier of D is “derived-declarator-type-list reference to T.” The optional attribute-specifier-seq appertains to the reference type. Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef-name (7.1.3, 14.1) or decltype-specifier (7.1.6.2), in which case the cv-qualifiers are ignored.

I've emboldened the relevant text. (I'd like to format the entire paragraph as it is in the standard, but I can't figure out how to get the right indentation and to add subscripting.)

Once the const disappears, normal reference collapsing kicks in as usual.



回答2:

A const T is a object of type T whose value cannot be modified. However, when T is a reference type, the const modifier is superfluous since references cannot be changed once initialized - they always refer to the same object. Thus, a const T when T=int& is just a T(which in this case is int&). Thus, the argument that f<int&> takes is a lvalue reference to an int&, which by c++11's collapsing rules is just int&.