<html>
<head>
<title>Core Issue 1512: Pointer comparison vs qualification conversions (revision 3)</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>
N3624<br/>
revision of N3498<br/>
Jens Maurer<br/>
2013-04-15

<h1>Core Issue 1512: Pointer comparison vs qualification conversions (revision 3)</h1>

<h2>Changes compared to N3478</h2>

<ul>
<li>Change "cv-compatible type" to "cv-combined type".</li>
<li>Only define "composite pointer type" for pointers and pointers-to-member</li>
<li>Minor typo fixes</li>
</ul>

<h2>Changes compared to N3498</h2>
<ul>
<li>Fix phrasing for comparison of array elements.</li>
<li>Cover all cases in the definition of &lt; and &gt; operators.</li>
</ul>

<h2>Introduction</h2>
This paper presents the modifications to the Working Draft necessary
to resolve core issues 583 and 1512.  In particular, it makes
<pre>
void f(char * p)
{
  if (p > 0) { ... }
  if (p > nullptr) { ... }
}
</pre>
ill-formed (both cases) and
<pre>
void g(int **p1, const int**p2)
{
   if (p1 == p2) { ... }
}
</pre>
well-formed.


<h2>Explanation of the changes</h2>

<p>
The changes below essentially replace all of 5.9 expr.rel and 5.10
expr.eq.  The current description in the Working Paper mixes semantic
constraints and results of relational operators with those of equality
operators.  Furthermore, the attempt at type unification for similar
types fails spectacularly.
</p>

<p>
The changes below define a generic composite pointer type that is
applicable to relational, equality, and conditional operators.  The
corresponding description is moved from section 5.9 expr.rel to clause
5 expr.
</p>

<p>
Furthermore, the semantic specifications of the relational and equality
operators are now phrased in terms of "compares greater" and "compares
equal"; the result of the operators is derived from these
specifications.  This avoids repetition and makes it possible to refer
to 5.10 expr.eq in order to determine the results of the &lt;= and
&gt;= operators for some cases.
</p>

<p>
For the equality operators, 5.10 expr.eq no longer refers to 5.9
expr.rel.  Instead, the relevant semantic constraints are specified
again (grouping, permissible types, result type).  Each paragraph then
enumerates one of the cases of types of operands (pointer, pointer to
member, std::nullptr_t, arithmetic or enumeration type).
</p>

<p>
The special wording about union members was phrased in terms of "same
address" and moved to 9.5 class.union, where layout of union members
is discussed.
</p>

<p>
Finally, the overload descriptions for built-in operators were
adjusted, because objects of type <code>std::nullptr_t</code> cannot
be used with relational operators any more.
</p>



<h2>Changes to the Working Paper</h2>

Add a new paragraph at the end of 5 expr:

<blockquote>
The <em>cv-combined type</em> of two types T1 and T2
is a type T3
similar to T1 whose cv-qualification signature (4.4 conv.qual) is:
<ul>
<li>for every j > 0, <em>cv</em><sub>3,j</sub> is the union of
<em>cv</em><sub>1,j</sub> and <em>cv</em><sub>2,j</sub></li>
<li>if the resulting <em>cv</em><sub>3,j</sub> is different from
<em>cv</em><sub>1,j</sub> or <em>cv</em><sub>2,j</sub>, then
<code>const</code> is added to every <em>cv</em><sub>3,k</sub> for 0
&lt; k &lt; j</li>
</ul>

[ Note: Given similar types T1 and T2, this
construction ensures that
both can be converted to T3. ]  The <em>composite pointer type</em> of
two operands p1 and
p2 having types T1 and T2, respectively, where at least one is a
pointer or pointer to member type or
<code>std::nullptr_t</code>, is:

<ul>
<li>if both p1 and p2 are null pointer constants,
<code>std::nullptr_t</code>;</li>
<li>if either p1 or p2 is a null pointer constant, T2 or T1,
respectively;</li>
<li>if T1 or T2 is "pointer to <em>cv1</em> <code>void</code>" and the
other type is "pointer to <em>cv2</em> T", "pointer to <em>cv12</em>
<code>void</code>" where <em>cv12</em> is the union of <em>cv1</em>
and <em>cv2</em>;</li>
<li>if T1 is "pointer to <em>cv1</em> C1" and T2 is "pointer to
<em>cv2</em> C2" where C1 is reference-related to C2 or C2 is
reference-related to C1 (8.5.3 dcl.init.ref), the cv-combined type
of T1 and T2 or the cv-combined type of T2 and T1,
respectively;</li>
<li>if T1 is "pointer to member of C1 of type cv1 U1" and T2 is
"pointer to member of C2 of type cv2 U2" where C1 is
reference-related to C2 or C2 is reference-related to C1 (8.5.3
dcl.init.ref), the cv-combined type of T2 and T1 or the cv-combined type
of T1 and T2, respectively;</li>
<li>if T1 and T2 are similar multi-level mixed pointer and pointer to
member types (4.4 expr.qual), the cv-combined type of T1 and
T2;</li>
<li>otherwise, a program that necessitates the determination of a
composite pointer type is ill-formed.</li>
</ul>

[ Example:
<pre>
typedef void *p;
typedef const int *q;
typedef int **pi;
typedef const int **pci;
</pre>
The composite pointer type of p and q is "pointer to const void"; the
composite pointer type of pi and pci is "pointer to const pointer to
const int".
]
</blockquote>

<em>The following also resolves core issue 583.</em>

<p>

Change in 5.9 expr.rel paragraphs 1 to 5:

<blockquote>
<p>
... The operands shall have arithmetic, enumeration, or pointer
type<del>, or type std::nullptr_t</del>.
</p>

<p>
The usual arithmetic conversions are performed on operands of
arithmetic or enumeration type.
<ins>If both operands are pointers, pointer</ins>  <del>Pointer</del>
conversions (4.10 conv.ptr)
and qualification conversions (4.4 conv.qual) are performed
<del>on pointer operands (or on a pointer operand and a null pointer
constant, or on two null pointer constants, at least one of which is
non-integral)</del> to
bring them to their <del><em>composite pointer type</em></del>
<ins>composite pointer type (clause 5 expr)</ins>.
<del>If one operand is a null
pointer constant, the composite pointer type is
<code>std::nullptr_t</code> if the other operand is also a null
pointer constant or, if the other operand is a pointer, the type of
the other operand. Otherwise, if one of the operands has type "pointer
to cv1 void," then the other has type "pointer to cv2 T " and the
composite pointer type is "pointer to cv12 void," where cv12 is the
union of cv1 and cv2. Otherwise, the composite pointer type is a
pointer type similar (4.4 conv.qual) to the type of one of the
operands, with a cv-qualification signature (4.4 conv.qual) that is
the union of the cv-qualification signatures of the operand types. [
Note: this implies that any pointer can be compared to a null pointer
constant and that any object pointer can be compared to a pointer to
(possibly cv-qualified) void. -- end note ] [ Example: </del>
</p>

<pre>
<del>void *p;
const int *q;
int **pi;
const int *const *pci;
void ct() {
  p <= q;           // Both converted to const void* before comparison
  pi <= pci;        // Both converted to const int *const * before comparison
}</del>
</pre>

<del>-- end example ]</del>

<p>
<ins>After conversions, the operands shall have the same type.</ins>
</p>

<p>
<del>Pointers to objects or functions of the same type (after
pointer conversions) can be compared, with a result</del>
<ins>Comparing pointers to objects is</ins> defined as
follows:

<ul>

<li><del>If two pointers p and q of the same type point to the same
object or function, or both point one past the end of the same array,
or are both null, then p&lt;=q and p&gt;=q both yield true and p&lt;q
and p>q both yield false.</del></li>

<li><del>If two pointers p and q of the same type point to
different
objects that are not members of the same object or elements of the
same array or to different functions, or if only one of them is null,
the results of p&lt;q, p>q, p&lt;=q, and p>=q are unspecified.</del></li>
<li><ins>If two pointers point to different elements
of the same array, or to subobjects
thereof, the pointer to the element with the higher
subscript compares greater.</ins></li>
<li><ins>If one pointer points to an element of an array, or
to a subobject thereof, and another pointer points one past the last
element of the array, the latter pointer compares
greater.</ins></li>
<li>If two pointers point to <ins>different</ins> non-static
data members of the same object, or to subobjects <del>or array
elements</del> of
such members, recursively, the pointer to the later declared member
compares greater
provided the two members have the same access control (Clause 11) and
provided their class is not a union.</li>
<li><del>If two pointers point to non-static data members of the same
object with different access control (Clause 11) the result is
unspecified.</del></li>

<li><del>If two pointers point to non-static data members of the same
union object, they compare equal (after conversion to void*, if
necessary). If two pointers point to elements of the same
array or one beyond the end of the array, the pointer to the object
with the higher subscript compares higher</del>.</li>

<li><del>Other pointer comparisons are unspecified.</del></li>
</ul>

<p>
<ins>If two operands p and q compare equal (5.10 expr.eq), p&lt;=q and
p&gt;=q both yield <code>true</code> and p&lt;q and p&gt;q both yield
<code>false</code>.  Otherwise, if a pointer p compares greater than a
pointer q, p&gt;=q, p&gt;q, q&lt;=p, and q&lt;p all
yield <code>true</code> and p&lt;=q, p&lt;q, q&gt;=p, and
q&gt;p all yield <code>false</code>. Otherwise, the result
of each of the operators is unspecified.</ins>
</p>

<p>
<del>Pointers to void (after pointer conversions) can be compared,
with a result defined as follows: If both pointers represent the same
address or are both the null pointer value, the result is true if the
operator is &lt;= or &gt;= and false otherwise; otherwise the result
is unspecified.</del>
</p>

<p>
<del>If two operands of type <code>std::nullptr_t</code> are compared, the
result is true if the operator is &lt;= or >=, and false otherwise.</del>
</p>
<p>
If both operands (after conversions) are of arithmetic or enumeration
type, each of the operators shall yield <code>true</code> if the
specified relationship is true and <code>false</code> if it is false.
</p>

</blockquote>

Change in 5.10 expr.eq paragraphs 1 to 4:

<blockquote>
<p>
The == (equal to) and the != (not equal to) operators <ins>group
left-to-right.  The operands shall have arithmetic, enumeration,
pointer, or pointer to member type, or type
<code>std::nullptr_t</code>.  The operators == and != both yield
<code>true</code> or <code>false</code>, i.e. a result of type
<code>bool</code>.</ins>

<del>have the same
semantic restrictions, conversions, and result type as the relational
operators except for their lower precedence and truth-value
result.
[ Note: a&lt;b == c&lt;d is true whenever a&lt;b and c&lt;d have the same
truth-value. -- end note ]</del>

<ins>In each case below, the operands shall have the same type after
the specified conversions have been applied.</ins>
</p>

<p>
<ins>If at least one of the operands is a pointer, pointer conversions (4.10
conv.ptr) and qualification conversions (4.4 conv.qual) are performed
on both operands to bring them to their composite pointer type (clause
5 expr).  Comparing pointers is defined as follows:</ins>

<del>Pointers of the same type (after pointer conversions) can be
compared for equality.</del> Two pointers <del>of the same type</del>
compare equal if
<del>and only if</del> they are both null, both point to the same
function, or both represent the same address (3.9.2
basic.compound)<ins>, otherwise they compare unequal</ins>.
</p>

<p>
<ins>If at least one of the operands is a pointer to member, pointer</ins>
<del>In addition, pointers to members can be compared, or a pointer to
member and a null pointer constant.  Pointer</del> to member
conversions (4.11 conv.mem) and qualification conversions (4.4
conv.qual) are performed <ins>on both operands</ins> to bring them to
<del>a common type</del> <ins>their composite pointer type (clause 5
expr)</ins>.

<del>If one operand is a null pointer constant, the common type is the
type of the other operand.  Otherwise, the common type is a pointer to
member type similar (4.4 conv.qual) to the type of one of the
operands, with a cv-qualification signature (4.4 conv.qual) that is
the union of the cv-qualification signatures of the operand types. [
Note: this implies that any pointer to member can be compared to a
null pointer constant. -- end note ]</del>

<ins>Comparing pointers to member is defined as follows:</ins>
<ul>
<li>If <del>both operands</del> <ins>two pointers to member</ins> are
<ins>both the</ins> null <ins>member pointer value</ins>, they compare
equal.</li>
<li><del>Otherwise if</del> <ins>If</ins> only one <ins>of two pointers to
member</ins> is <ins>the</ins> null <ins>member pointer value</ins>,
they compare unequal.</li>
<li><del>Otherwise if</del> <ins>If</ins> either is a pointer to a
virtual member function, the result is unspecified.</li>
<li><del>Otherwise they</del> <ins>Two pointers to member</ins>
compare equal if <del>and only if</del> they would refer to the same
member of the same most derived object (1.8 intro.object) or the
same subobject
if they were dereferenced with a hypothetical object of the associated
class type<ins>, otherwise they compare unequal</ins>.  [ Example:
<pre>
   struct B {
     int f();
   };
   struct L : B { };
   struct R : B { };
   struct D : L, R { };
   int (B::*pb)() = &amp;B::f;
   int (L::*pl)() = pb;
   int (R::*pr)() = pb;
   int (D::*pdl)() = pl;
   int (D::*pdr)() = pr;
   bool x = (pdl == pdr);             // false
</pre>
 -- end example ]
</li>
</ul>

<p>
<del>If two</del> <ins>Two</ins> operands of type
<code>std::nullptr_t</code> <ins> or one operand of type
<code>std::nullptr_t</code> and the other a null pointer
constant compare equal</ins> <del>are
compared, the result is true if the operator is ==, and false
otherwise</del>.
</p>

<p>
<ins>If two operands compare equal, the result is <code>true</code>
for operator== and <code>false</code> for operator!=.  If two operands
compare unequal, the result is <code>false</code> for operator== and
<code>true</code> for operator!=.  Otherwise, the result of each of
the operators is unspecified.</ins>
</p>

<p>
<ins>If both operands are of arithmetic or enumeration
type, the usual arithmetic conversions are performed on both
operands;
each</ins> <del>Each</del> of the operators shall yield true if
the specified relationship is true and false if it is false.
</p>

</blockquote>


Change in 5.16 expr.cond paragraph 6:

<blockquote>
<ul>
<li>...</li>
<li><ins>One or both of the</ins> <del>The</del> second and third
operands have pointer type<del>, or one has
pointer type and the other is a null pointer constant, or both are
null pointer constants, at least one of which is non-integral</del>;
pointer
conversions (4.10 conv.ptr) and qualification conversions (4.4
conv.qual) are performed to bring them to their composite pointer type
<del>(5.9 expr.rel)</del> <ins>(5 expr)</ins>. The result is of
the composite pointer type.</li>
<li><ins>One or both of the</ins> <del>The</del> second and third operands have pointer to member type<del>, or one
has pointer to member type and the other is a null pointer
constant</del>;
pointer to member conversions (4.11 conv.mem) and qualification
conversions (4.4 conv.qual) are performed to bring them to <del>a common
type, whose cv-qualification shall match the cv-qualification  of
either the second or the third operand</del> <ins>their composite
pointer type (5 expr)</ins>. The result is of the
<del>common</del>
<ins>composite pointer</ins> type.</li>
<li><ins>Both the second and third operands have type
<code>std::nullptr_t</code> or one has that type and the other is a
null pointer constant.  The result is of type
<code>std::nullptr_t</code>.</ins></li>
</ul>

</blockquote>

Change in 9.5 class.union paragraph 1:
<blockquote>
... Each non-static data member is allocated as if it were the sole
member of a struct. <ins>All non-static data members of a union object
have the same address.</ins> ...
</blockquote>


Change in 13.6 over.built paragraphs 15 and 16:

<blockquote>
For every T , where T is an enumeration type<del>,</del> <ins>or</ins>
a pointer type<del>, or <code>std::nullptr_t</code></del>, there exist
candidate operator functions of the form
<pre>
  bool     operator<(T , T );
  bool     operator>(T , T );
  bool     operator<=(T , T );
  bool     operator>=(T , T );
  bool     operator==(T , T );
  bool     operator!=(T , T );
</pre>
For every pointer to member type T <ins>or type
<code>std::nullptr_t</code></ins> there exist candidate operator
functions of the form
<pre>
  bool     operator==(T , T );
  bool     operator!=(T , T );
</pre>

</blockquote>

Change in 20.7.2.2.7 util.smartptr.shared.cmp paragraph 2:

<blockquote>
<em>Returns:</em> <code>less&lt;V>()(a.get(), b.get())</code>, where V
is the composite pointer type <del>(5.9 expr.rel)</del> <ins>(5
expr)</ins> of T* and U*.
</blockquote>


</body>
</html>
