<html>
<head>
<title>N4261: 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>
N4261<br/>
revision of N4178<br/>
Jens Maurer &lt;Jens.Maurer@gmx.net><br/>
2014-11-06

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

<h2>Introduction</h2>

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

<p>
Compared to N4178, it restores the prohibition against casting away
constness in <code>reinterpret_cast</code>.
</p>


<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 three
paragraphs:

<blockquote>

<p>
<ins>A <em>cv-decomposition</em> of a type <code>T</code> is a
sequence of cv<sub>i</sub> and P<sub>i</sub> such that <code>T</code>
is cv<sub>0</sub> P<sub>0</sub> cv<sub>1</sub> P<sub>1</sub> . . .
cv<sub>n-1</sub> P<sub>n-1</sub> cv<sub>n</sub> U for n > 0, where
each cv<sub>i</sub> is a set of cv-qualifiers (3.9.3
basic.type.qualifier), each P<sub>i</sub> is "pointer to"
(8.3.1 dcl.ptr), "pointer to member of class C<sub>i</sub> of type"
(8.3.3 dcl.mptr), "array of N<sub>i</sub>", or "array of unknown bound
of" (8.3.4 dcl.array). If P<sub>i</sub> designates an array, the
cv-qualifiers cv<sub>i+1</sub> on the element type are also taken as
the cv-qualifiers cv<sub>i</sub> of the array. [ Example: The type
denoted by the <em>type-id</em> "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</code>, that is,
cv<sub>1</sub>, cv<sub>2</sub>, ... cv<sub>n</sub>, is called the
<em>cv-qualification signature</em> of <code>T</code>.</ins>
</p>


<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
<del>there exists a type T and integer n > 0 such that:</del>
<ins>they have cv-decompositions with the same n such that
corresponding P<sub>i</sub> components are the same and the types
denoted by U are the same.</ins>

<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/>
</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><br/>
</blockquote>

<del>where each cv<sub>i,j</sub> is <code>const</code>,
<code>volatile</code>, <code>const volatile</code>, 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 <em>cv-qualification signature</em> of the
pointer type.</del>

<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>, where
cv<sub>i</sub><sup>j</sup> denotes the cv-qualifiers in the
cv-qualification signature of T<sub>j</sub> [ 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 <del>j</del> <ins>i</ins> > 0,
if <code>const</code> is in <del>cv<sub>1,j</sub></del>
<ins>cv<sub>i</sub><sup>1</sup></ins> then <code>const</code> is in
<del>cv<sub>2,j</sub></del> <ins>cv<sub>i</sub><sup>2</sup></ins>, and
similarly for <code>volatile</code>.</li>

<li><del>if</del> <ins>If</ins> the <del>cv<sub>1,j</sub></del>
<ins>cv<sub>i</sub><sup>1</sup></ins> and <del>cv<sub>2,j</sub></del>
<ins>cv<sub>i</sub><sup>2</sup></ins> are different, then
<code>const</code> is in every <del>cv<sub>2,k</sub></del>
<ins>cv<sub>k</sub><sup>2</sup></ins> for 0 &lt; k &lt; <del>j</del>
<ins>i</ins>.</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>

Do not change 5.2.10 expr.reinterpret.cast paragraph 2:

<blockquote>
The <code>reinterpret_cast</code> 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.

</blockquote>

Do not change the footnotes in 5.2.10 expr.reinterpret.cast paragraphs 7 and 10:

<blockquote>
[ 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. ]

</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 &&r1 = const_cast&lt;A>(CA{});   // error: temporary array decayed to pointer
A &&r2 = 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 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 cv<sub>0</sub><sup>2</sup>
P<sub>0</sub><sup>2</sup> cv<sub>1</sub><sup>2</sup>
P<sub>1</sub><sup>2</sup> . . . cv<sub>n-1</sub><sup>2</sup>
P<sub>n-1</sub><sup>2</sup> cv<sub>n</sub><sup>2</sup>
U<sup>2</sup>, and
there is no qualification conversion that converts T1 to
cv<sub>0</sub><sup>2</sup> P<sub>0</sub><sup>1</sup>
cv<sub>1</sub><sup>2</sup> P<sub>1</sub><sup>1</sup>
. . . cv<sub>n-1</sub><sup>2</sup> P<sub>n-1</sub><sup>1</sup>
cv<sub>n</sub><sup>2</sup> U<sup>1</sup>.</ins> </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>

Do not 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>,</li>
<li>a <code>reinterpret_cast</code> (5.2.10), or</li>
<li>a <code>reinterpret_cast</code> followed by a const_cast,</li>
</ul>

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

</body>
</html>
