Title
Propagation of cv-qualifiers in reference-to-reference collapse
Status
cd1
Section
9.8.2 [namespace.def]
Submitter
Russell Yanofsky

Created on 2005-09-24.00:00:00 last changed 189 months ago

Messages

Date: 2006-10-15.00:00:00

[Voted into the WP at the October, 2006 meeting as part of paper J16/06-0188 = WG21 N2118.]

Date: 2006-02-15.00:00:00

Note (February, 2006):

The wording included in the rvalue-reference paper, J16/06-0022 = WG21 N1952, incorporates changes intended to implement the October, 2005 consensus of the CWG.

Date: 2005-10-15.00:00:00

Notes from the October, 2005 meeting:

The consensus of the CWG was that the resolution of issue 106 should be revised not to propagate embedded cv-qualification.

Date: 2008-06-29.00:00:00

The resolution of issue 106 specifies that an attempt to create a type “reference to cv1 T,” where T is a typedef or template parameter of the type “reference to cv2 S,” actually creates the type “reference to cv12 S,” where cv12 is the union of the two sets of cv-qualifiers.

One objection that has been raised to this resolution is that it is inconsistent with the treatment of cv-qualification and references specified in 9.3.4.3 [dcl.ref] paragraph 1, which says that cv-qualifiers applied to a typedef or template argument that is a reference type are ignored. For example:

    typedef int& intref;
    const intref r1;       // reference to int
    const intref& r2;      // reference to const int

In fact, however, these two declarations are quite different. In the declaration of r1, const applies to a “top-level” reference, while in the declaration of t2, it occurs under a reference. In general, cv-qualifiers that appear under a reference are preserved, even if the type appears in a context in which top-level cv-qualification is removed, for example, in determining the type of a function from parameter types (9.3.4.6 [dcl.fct] paragraph 3) and in template argument deduction (13.10.3.2 [temp.deduct.call] paragraph 2).

Another objection to the resolution is that type composition gives different results in a single declaration than it does when separated into two declarations. For example:

    template <class T>
    struct X {
       typedef T const T_const;
       typedef T_const& type1;
       typedef T const& type2;
    };

    X<int&>::type1 t1;    // int&
    X<int&>::type2 t2;    // int const&

The initial motivation for the propagation of cv-qualification during reference-to-reference collapse was to prevent inadvertent loss of cv-qualifiers in contexts in which it could make a difference. For example, if the resolution were changed to discard, rather than propagate, embedded cv-qualification, overload resolution could surprisingly select a non-const version of a member function:

   struct X {
       void g();
       void g() const;
   };

   template <typename T> struct S {
       static void f(const T& t) {
           t.g();    // const or non-const???
       }
   };

   X x;

   void q() {
       S<X>::f(x);    // calls X::g() const
       S<X&>::f(x);   // calls X::g()
   }

Another potentially-surprising outcome of dropping embedded cv-qualifiers would be:

   template <typename T> struct A {
       void f(T&);          // mutating version
       void f(const T&);    // non-mutating version
   };

   A<int&> ai;    // Ill-formed: A<int&> declares f(int&) twice

On the other hand, those who would like to see the resolution changed to discard embedded cv-qualifiers observe that these examples are too simple to be representative of real-world code. In general, it is unrealistic to expect that a template written with non-reference type parameters in mind will automatically work correctly with reference type parameters as a result of applying the issue 106 resolution. Instead, template metaprogramming allows the template author to choose explicitly whether cv-qualifiers are propagated or dropped, according to the intended use of the template, and it is more important to respect the reasonable intuition that a declaration involving a template parameter will not change the type that the parameter represents.

As a sample of real-world code, tr1::tuple was examined. In both cases — the current resolution of issue 106 and one in which embedded cv-qualifiers were dropped — some metaprogramming was required to implement the intended interface, although the version reflecting the revised resolution was somewhat simpler.

History
Date User Action Args
2008-10-05 00:00:00adminsetstatus: wp -> cd1
2008-06-29 00:00:00adminsetmessages: + msg1723
2007-05-06 00:00:00adminsetstatus: dr -> wp
2006-11-05 00:00:00adminsetstatus: drafting -> dr
2006-04-22 00:00:00adminsetstatus: open -> drafting
2006-02-24 00:00:00adminsetmessages: + msg1312
2005-12-16 00:00:00adminsetmessages: + msg1293
2005-09-24 00:00:00admincreate