<html>
<head>
<title>N4178: Proposed resolution for Core Issue 330: Qualification conversions and pointers to arrays of pointers </title>

<style type="text/css">
  ins { text-decoration:none; font-weight:bold; background-color:#A0FFA0 }
  del { text-decoration:line-through; background-color:#FFA0A0 }
  strong { font-weight: inherit; color: #2020ff }
</style>

</head>

<body>
N4178<br/>
Jens Maurer<br/>
2014-10-07

<h1>N4178: Proposed resolution for Core Issue 330: Qualification conversions and pointers to arrays of pointers </h1>

<h2>Introduction</h2>

This paper presents the proposed resolution for
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#330">core issue 330</a>
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 <code>reinterpret_cast</code> so that it may
now cast away constness.  For example,

<pre>
reinterpret_cast&lt;int *>((const void *)0)
</pre>

is ill-formed in C++14 due to the provision in 5.2.10
[expr.reinterpret.cast] paragraph 2 "The <code>reinterpret_cast</code>
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
<code>reinterpret_cast</code>, the restriction on casting away
constness appeared as a rather odd constraint.

<h2>The issue</h2>

<p>
<em>The following is a verbatim copy of the issue writeup in the core
issues list.</em>
</p>

<p>
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.
</p>

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

<pre>
  int main()
  {
     double *array2D[2][3];
  
     double       *       (*array2DPtr1)[3] = array2D;     // Legal
     double       * const (*array2DPtr2)[3] = array2DPtr1; // Legal
     double const * const (*array2DPtr3)[3] = array2DPtr2; // Illegal
  }
</pre>

and compare this code with:-

<pre>
  int main()
  {
     double *array[2];
  
     double       *       *ppd1 = array; // legal
     double       * const *ppd2 = ppd1;  // legal
     double const * const *ppd3 = ppd2;  // certainly legal (4.4/4)
  }
</pre>

<p>
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"
</p>

<p>
It appears that <code>reinterpret_cast</code> is the only way to
perform the conversion.
</p>

<h2>Wording changes</h2>

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

<blockquote>
<del>A conversion can add cv-qualifiers at levels other than the first in
multi-level pointers, subject to the following rules: [ Footnote ... ]</del>

Two <del>pointer</del> types T1 and T2 are <em>similar</em> if there exists a type T and integer n > 0 such that:
<blockquote>
<del>T1 is cv 1,0 pointer to cv 1,1 pointer to . . . cv 1,n-1 pointer to cv 1,n T</del><br/>
<ins>T1 is cv<sub>1,0</sub> P<sub>0</sub> cv<sub>1,1</sub> P<sub>1</sub> . . .  cv<sub>1,n-1</sub> P<sub>n-1</sub> cv<sub>1,n</sub> T</ins>
</blockquote>
and
<blockquote>
<del>T2 is cv 2,0 pointer to cv 2,1 pointer to . . . cv 2,n-1 pointer to cv 2,n T</del><br/>
<ins>T2 is cv<sub>2,0</sub> P<sub>0</sub> cv<sub>2,1</sub> P<sub>1</sub> . . .  cv<sub>2,n-1</sub> P<sub>n-1</sub> cv<sub>2,n</sub> T</ins>
</blockquote>

where each cv<sub>i,j</sub> is <code>const</code>,
<code>volatile</code>, <code>const volatile</code>, or nothing
<ins>and, for each j from 0 ... n-1, P<sub>j</sub> P<sub>j+1</sub> ... P<sub>n-1</sub> T is a pointer, pointer to member, or
array type</ins>. The n-tuple of cv-qualifiers after the
first <ins>one</ins> <del>in a pointer type e.g., cv
1,1 , cv 1,2 , . . .  , cv 1,n in the pointer type T1</del>, <ins>that
is, cv<sub>i,1</sub>, cv<sub>i,2</sub>, ... cv<sub>i,n</sub> for
Ti,</ins> is called the <em>cv-qualification signature</em> of the
<del>pointer</del> type.

<p>
<del>An</del> <ins>A prvalue</ins> expression of type T1 can be converted to type T2 if
<del>and only if</del> the following conditions are
satisfied <ins>[ Footnote: These rules ensure that const-safety is
preserved by the conversion. ]</ins>:

<ul>
<li><del>the pointer types</del> <ins>T1 and T2</ins> are
similar.</li>

<li><del>for</del> <ins>For</ins> every j > 0, if <code>const</code>
is in cv<sub>1,j</sub> then <code>const</code> is in<sub>cv 2,j
</sub>, and similarly for <code>volatile</code>.</li>

<li><del>if</del> <ins>If</ins> the cv<sub>1,j</sub> and
cv<sub>2,j</sub> are different, then <code>const</code> is in every
cv<sub>2,k</sub> for 0 &lt; k &lt; j.</li>

</ul>

[ 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,
<pre>
     int main() {
         const char c = 'c';
         char* pc;
         const char** pcc = &pc;                // #1: not allowed
         *pcc = &c;
         *pc = 'C';                             // #2: modifies a const object
     }
</pre>
-- end note ]
<p>
</blockquote>

Remove 4.4 conv.qual paragraphs 5-7:

<blockquote>
<del>A multi-level pointer to member type, or a multi-level mixed
pointer and pointer to member type has the form: ...</del>

<p>
<del>Two multi-level pointer to member types or two multi-level mixed
pointer and pointer to member types T1 and T2 are similar if ...</del>

<p>
<del>For similar multi-level pointer to member types and similar
multi-level mixed pointer and pointer to member types, ...</del>

</blockquote> 

Move 4.4 conv.qual paragraphs 1-3 after paragraph 4 and turn them into notes:

<blockquote>
<ins>[ Note:</ins> 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". <ins>--
end note ]</ins>
<p>

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

</blockquote>

Change in 5 expr paragraph 13:

<blockquote>
<ul>

<li>...</li>

<li>if T1 and T2 are similar <del>multi-level mixed pointer and
pointer to member</del> types (4.4 conv.qual), the cv-combined type of
T1 and T2;</li>

<li>...</li>

</ul>

</blockquote>

Change in 5.2.10 expr.reinterpret.cast paragraph 2:

<blockquote>
<del>The <code>reinterpret_cast</code> operator shall not cast
away constness (5.2.11 expr.const.cast).</del> 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.

</blockquote>

Delete the footnotes in 5.2.10 expr.reinterpret.cast paragraphs 7 and 10:

<blockquote>
<del>[ Footnote: The types may have different cv-qualifiers, subject
to the overall restriction that a reinterpret_cast cannot cast away
constness. ]</del> ...

<del>[ Footnote: T1 and T2 may have different cv-qualifiers, subject
to the overall restriction that a reinterpret_cast cannot cast away
constness. ]</del>

</blockquote>



Change in 5.2.11 expr.const.cast paragraph 3:

<blockquote>
For two <del>pointer</del> <ins>similar</ins> types T1 and T2 <ins>(4.4 conv.qual)</ins> <del>where</del>
<blockquote>
  <del>T1 is cv 1,0 pointer to cv 1,1 pointer to . . . cv 1,n-1 pointer to cv 1,n T</del>
</blockquote>
<del>and</del>
<blockquote>
   <del>T2 is cv 2,0 pointer to cv 2,1 pointer to . . . cv 2,n-1 pointer to cv 2,n T</del>
</blockquote>

<del>where T is any object type or the void type and where cv 1,k and
cv 2,k may be different cv-qualifications</del></del>, a prvalue of
type T1 may be explicitly converted to the type T2 using a
<code>const_cast</code>. The result of a <del>pointer</del>
<code>const_cast</code> refers to the original <del>object</del>
<ins>entity</ins>. <ins>[ Example:</ins>
<pre>
<ins>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&lt;A>(CA{});   // error: temporary array decayed to pointer
A &&r = const_cast&lt;A&&>(CA{}); // ok</ins>
</pre>
<ins>-- end example ]</ins>

</blockquote>


Remove in 5.2.11 expr.const.cast paragraph 5:

<blockquote>
<del>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.</del>

</blockquote>


Replace all of 5.2.11 expr.const.cast paragraph 8:

<blockquote>
<del>The following rules define the process known as casting away constness. In these rules Tn and Xn represent
types. For two pointer types:</del>
<pre><del>
      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 )</del>
</pre>
<del>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:</del>

<blockquote>
   <del>Tcv 1,(N -K+1) * cv 1,(N -K+2) * . . . cv 1,N *</del>
</blockquote>
<del>to</del>
<blockquote>
   <del>Tcv 2,(M -K+1) * cv 2,(M -K+2) * . . . cv 2,M *</del>
</blockquote>


<ins>A conversion from a type T1 to a type T2 <em>casts away
constness</em> if</ins>

<ul>

<li><ins>T1 and T2 are similar and there is no qualification
conversion (4.4 conv.qual) that converts T1 to T2,</ins></li>

<li><ins>T1 is "pointer to cv1 U1" and T2 is "pointer to cv2
U2" and there is no qualification conversion that converts T1 to the
type "pointer to cv2 U1" (see 4.10 conv.ptr), or</ins></li>

<li><ins>T1 is "pointer to member of class C1 of type cv1 U1" and T2 is
"pointer to member of class C2 of type cv2 U2" and there is no
qualification conversion that converts T1 to the type "pointer to
member of class C1 of type cv2 U1" (see 4.11
conv.mem).</ins></li>

</ul>

</blockquote>

Remove 5.2.11 expr.const.cast paragraphs 11 and 12:

<blockquote>
<del>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.</del>
<p>

<del>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.</del>

</blockquote>

Change 5.4 expr.cast paragraph 4:

<blockquote>
The conversions performed by
<ul>
<li>...</li>
<li>a <code>static_cast</code> followed by a <code>const_cast</code>, <ins>or</ins></li>
<li>a <code>reinterpret_cast</code> (5.2.10), <del>or</del></li>
<li><del>a <code>reinterpret_cast</code> followed by a const_cast,</del></li>
</ul>

can be performed using the cast notation of explicit type conversion. ...
</blockquote>

</body>
</html>
