7

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?

KnowItAllWannabe
  • 12,972
  • 8
  • 50
  • 91
  • 1
    Related to [c98-03-reference-collapsing-and-cv-qualifiers](http://stackoverflow.com/questions/14761581/c98-03-reference-collapsing-and-cv-qualifiers) – Jarod42 Jan 01 '15 at 01:15
  • http://www-01.ibm.com/support/knowledgecenter/SS2LWA_12.1.0/com.ibm.xlcpp121.bg.doc/language_ref/reference_collapsing.html has 2 tables for Reference collapsing. – Jarod42 Jan 01 '15 at 01:28
  • Related/duplicate: [Reference collapsing?](http://stackoverflow.com/q/3771208) – dyp Jan 01 '15 at 01:54
  • http://stackoverflow.com/questions/27256516/c-template-function-with-explicitly-specified-reference-type-as-type-paramete/27256623#27256623 – T.C. Jan 01 '15 at 02:10

2 Answers2

4

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 [dcl.ref], which says:

In a declaration T D where D has either of the forms

& attribute-specifier-seqopt D1
&& attribute-specifier-seqopt D1

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 ([dcl.typedef], [temp.param]) or decltype-specifier ([dcl.type.simple]), 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.

RexYuan
  • 280
  • 7
  • 13
KnowItAllWannabe
  • 12,972
  • 8
  • 50
  • 91
  • This seems to be the correct answer. Note that in http://stackoverflow.com/a/14761809/777186, Yakk derived it from 8.3.2/5 (which used to be 14.3.2/4 in pre-release versions of C++11). – jogojapan Jan 01 '15 at 05:29
3

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&.

Pradhan
  • 16,391
  • 3
  • 44
  • 59
  • 1
    Can you give some guidance about where in the standard this behavior is specified, i.e., where it says that const T is T when T is a reference type? – KnowItAllWannabe Jan 01 '15 at 01:25