N4178
Jens Maurer
2014-10-07

N4178: 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 converions. Other than allowing the qualification conversions asked for in the issue, it also changes reinterpret_cast so that it may now cast away constness. For example,
reinterpret_cast<int *>((const void *)0)
is ill-formed in C++14 due to the provision in 5.2.10 [expr.reinterpret.cast] paragraph 2 "The reinterpret_cast operator shall not cast away constness (5.2.11 expr.const.cast)". The wording changes below will remove this restriction, thus the example above will become well-formed. Given the general nature of reinterpret_cast, the restriction on casting away constness appeared as a rather odd constraint.

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 two paragraphs:
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:
T1 is cv 1,0 pointer to cv 1,1 pointer to . . . cv 1,n-1 pointer to cv 1,n T
T1 is cv1,0 P0 cv1,1 P1 . . . cv1,n-1 Pn-1 cv1,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
T2 is cv2,0 P0 cv2,1 P1 . . . cv2,n-1 Pn-1 cv2,n T
where each cvi,j is const, volatile, const volatile, or nothing and, for each j from 0 ... n-1, Pj Pj+1 ... Pn-1 T is a pointer, pointer to member, or array type. The n-tuple of cv-qualifiers after the first one in a pointer type e.g., cv 1,1 , cv 1,2 , . . . , cv 1,n in the pointer type T1, that is, cvi,1, cvi,2, ... cvi,n for Ti, 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 [ 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:
Change in 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.
Delete 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 &&r = const_cast<A>(CA{});   // error: temporary array decayed to pointer
A &&r = 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
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.

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