N4261
revision of N4178
Jens Maurer <Jens.Maurer@gmx.net>
2014-11-06

N4261: Proposed resolution for Core Issue 330: Qualification conversions and pointers to arrays of pointers

Introduction

This paper presents the proposed resolution for core issue 330 as reviewed in teleconferences of WG21's Core Working Group, substantially cleaning up the wording around qualification conversions.

Compared to N4178, it restores the prohibition against casting away constness in reinterpret_cast.

The issue

The following is a verbatim copy of the issue writeup in the core issues list.

Section 4.4 [conv.qual] covers the case of multi-level pointers, but does not appear to cover the case of pointers to arrays of pointers. The effect is that arrays are treated differently from simple scalar values.

Consider for example the following code: (from the thread "Pointer to array conversion question" begun in comp.lang.c++.moderated)

  int main()
  {
     double *array2D[2][3];
  
     double       *       (*array2DPtr1)[3] = array2D;     // Legal
     double       * const (*array2DPtr2)[3] = array2DPtr1; // Legal
     double const * const (*array2DPtr3)[3] = array2DPtr2; // Illegal
  }
and compare this code with:-
  int main()
  {
     double *array[2];
  
     double       *       *ppd1 = array; // legal
     double       * const *ppd2 = ppd1;  // legal
     double const * const *ppd3 = ppd2;  // certainly legal (4.4/4)
  }

The problem appears to be that the pointed to types in example 1 are unrelated since nothing in the relevant section of the standard covers it - 4.4 [conv.qual] does not mention conversions of the form "cv array of N pointer to T" into "cv array of N pointer to cv T"

It appears that reinterpret_cast is the only way to perform the conversion.

Wording changes

Change 4.4 conv.qual paragraphs 4 and split it into three paragraphs:

A cv-decomposition of a type T is a sequence of cvi and Pi such that T is cv0 P0 cv1 P1 . . . cvn-1 Pn-1 cvn U for n > 0, where each cvi is a set of cv-qualifiers (3.9.3 basic.type.qualifier), each Pi is "pointer to" (8.3.1 dcl.ptr), "pointer to member of class Ci of type" (8.3.3 dcl.mptr), "array of Ni", or "array of unknown bound of" (8.3.4 dcl.array). If Pi designates an array, the cv-qualifiers cvi+1 on the element type are also taken as the cv-qualifiers cvi of the array. [ Example: The type denoted by the type-id "const int **" has two cv-decompositions, taking U as "int" and as "pointer to const int". ] The n-tuple of cv-qualifiers after the first one in the longest cv-decomposition of T, that is, cv1, cv2, ... cvn, is called the cv-qualification signature of T.

A conversion can add cv-qualifiers at levels other than the first in multi-level pointers, subject to the following rules: [ Footnote ... ] Two pointer types T1 and T2 are similar if there exists a type T and integer n > 0 such that: they have cv-decompositions with the same n such that corresponding Pi components are the same and the types denoted by U are the same.
T1 is cv 1,0 pointer to cv 1,1 pointer to . . . cv 1,n-1 pointer to cv 1,n T
and
T2 is cv 2,0 pointer to cv 2,1 pointer to . . . cv 2,n-1 pointer to cv 2,n T
where each cvi,j is const, volatile, const volatile, or nothing . The n-tuple of cv-qualifiers after the first in a pointer type e.g., cv 1,1 , cv 1,2 , . . . , cv 1,n in the pointer type T1 is called the cv-qualification signature of the pointer type.

An A prvalue expression of type T1 can be converted to type T2 if and only if the following conditions are satisfied, where cvij denotes the cv-qualifiers in the cv-qualification signature of Tj [ Footnote: These rules ensure that const-safety is preserved by the conversion. ]:

[ Note: if a program could assign a pointer of type T** to a pointer of type const T** (that is, if line #1 below were allowed), a program could inadvertently modify a const object (as it is done on line #2). For example,
     int main() {
         const char c = 'c';
         char* pc;
         const char** pcc = &pc;                // #1: not allowed
         *pcc = &c;
         *pc = 'C';                             // #2: modifies a const object
     }
-- end note ]

Remove 4.4 conv.qual paragraphs 5-7:
A multi-level pointer to member type, or a multi-level mixed pointer and pointer to member type has the form: ...

Two multi-level pointer to member types or two multi-level mixed pointer and pointer to member types T1 and T2 are similar if ...

For similar multi-level pointer to member types and similar multi-level mixed pointer and pointer to member types, ...

Move 4.4 conv.qual paragraphs 1-3 after paragraph 4 and turn them into notes:
[ Note: A prvalue of type "pointer to cv1 T" can be converted to a prvalue of type "pointer to cv2 T" if "cv2 T" is more cv-qualified than "cv1 T". A prvalue of type "pointer to member of X of type cv1 T" can be converted to a prvalue of type "pointer to member of X of type cv2 T" if "cv2 T" is more cv-qualified than "cv1 T". -- end note ]

[ Note: Function types (including those used in pointer to member function types) are never cv-qualified (8.3.5). -- end note ]

Change in 5 expr paragraph 13:
Do not change 5.2.10 expr.reinterpret.cast paragraph 2:
The reinterpret_cast operator shall not cast away constness (5.2.11 expr.const.cast). An expression of integral, enumeration, pointer, or pointer-to-member type can be explicitly converted to its own type; such a cast yields the value of its operand.
Do not change the footnotes in 5.2.10 expr.reinterpret.cast paragraphs 7 and 10:
[ Footnote: The types may have different cv-qualifiers, subject to the overall restriction that a reinterpret_cast cannot cast away constness. ] ... [ Footnote: T1 and T2 may have different cv-qualifiers, subject to the overall restriction that a reinterpret_cast cannot cast away constness. ]
Change in 5.2.11 expr.const.cast paragraph 3:
For two pointer similar types T1 and T2 (4.4 conv.qual) where
T1 is cv 1,0 pointer to cv 1,1 pointer to . . . cv 1,n-1 pointer to cv 1,n T
and
T2 is cv 2,0 pointer to cv 2,1 pointer to . . . cv 2,n-1 pointer to cv 2,n T
where T is any object type or the void type and where cv 1,k and cv 2,k may be different cv-qualifications, a prvalue of type T1 may be explicitly converted to the type T2 using a const_cast. The result of a pointer const_cast refers to the original object entity. [ Example:
typedef int *A[3];               // array of 3 pointer to int
typedef const int *const CA[3];  // array of 3 const pointer to const int

CA &&r = A{}; // ok, reference binds to temporary array object after qualification conversion to type CA
A &&r1 = const_cast<A>(CA{});   // error: temporary array decayed to pointer
A &&r2 = const_cast<A&&>(CA{}); // ok
-- end example ]
Remove in 5.2.11 expr.const.cast paragraph 5:
For a const_cast involving pointers to data members, multi-level pointers to data members and multi-level mixed pointers and pointers to data members (4.4 conv.qual), the rules for const_cast are the same as those used for pointers; the "member" aspect of a pointer to member is ignored when determining where the cv-qualifiers are added or removed by the const_cast. The result of a pointer to data member const_cast refers to the same member as the original (uncast) pointer to data member.
Replace all of 5.2.11 expr.const.cast paragraph 8:
The following rules define the process known as casting away constness. In these rules Tn and Xn represent types. For two pointer types:

      X1 is T1cv 1,1 * . . . cv 1,N * where T1 is not a pointer type
      X2 is T2cv 2,1 * . . . cv 2,M * where T2 is not a pointer type
      K is min(N, M )
casting from X1 to X2 casts away constness if, for a non-pointer type T there does not exist an implicit conversion (Clause 4) from:
Tcv 1,(N -K+1) * cv 1,(N -K+2) * . . . cv 1,N *
to
Tcv 2,(M -K+1) * cv 2,(M -K+2) * . . . cv 2,M *
A conversion from a type T1 to a type T2 casts away constness if T1 and T2 are different, there is a cv-decomposition (4.4 conv.qual) of T1 yielding n such that T2 has a cv-decomposition of the form cv02 P02 cv12 P12 . . . cvn-12 Pn-12 cvn2 U2, and there is no qualification conversion that converts T1 to cv02 P01 cv12 P11 . . . cvn-12 Pn-11 cvn2 U1.
Remove 5.2.11 expr.const.cast paragraphs 11 and 12:
Casting from a prvalue of type "pointer to data member of X of type T1" to the type "pointer to data member of Y of type T2" casts away constness if a cast from a prvalue of type "pointer to T1" to the type "pointer to T2" casts away constness.

For multi-level pointer to members and multi-level mixed pointers and pointer to members (4.4), the "mem- ber" aspect of a pointer to member level is ignored when determining if a const cv-qualifier has been cast away.

Do not change 5.4 expr.cast paragraph 4:
The conversions performed by can be performed using the cast notation of explicit type conversion. ...