void f(char * p) { if (p > 0) { ... } if (p > nullptr) { ... } }ill-formed (both cases) and
void g(int **p1, const int**p2) { if (p1 == p2) { ... } }well-formed.
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.
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.
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 <= and >= operators for some cases.
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).
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.
Finally, the overload descriptions for built-in operators were
adjusted, because objects of type std::nullptr_t
cannot
be used with relational operators any more.
The cv-compatible type of two types T1 and T2 is a type T3 similar to T1 whose cv-qualification signature (4.4 conv.qual) is:The following also resolves core issue 583.[ Note: This construction ensures that both T1 and T2 can be converted to T3. ] The composite pointer type of two operands p1 and p2 having types T1 and T2, respectively, is:
- for every j > 0, cv3,j is the union of cv1,j and cv2,j
- if the resulting cv3,j is different from cv1,j or cv2,j, then
const
is added to every cv3,k for 0 < k < j[ Example:
- if both p1 and p2 are null pointer constants,
std::nullptr_t
;- if either p1 or p2 is a null pointer constant, T2 or T1, respectively;
- if T1 or T2 is "pointer to cv1
void
" and the other type is "pointer to cv2 T", "pointer to cv12void
" where cv12 is the union of cv1 and cv2;- if T1 is "pointer to cv1 C1" and T2 is "pointer to cv2 C2" where C1 is reference-related to C2 or C2 is reference-related to C1 (8.5.3 dcl.init.ref), the cv-compatible type of T1 and T2 or the cv-compatible type of T2 and T1, respectively;
- 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-compatible type of T2 and T1 or the cv-compatible type of T1 and T2, respectively;
- if T1 and T2 are similar multi-level mixed pointer and pointer to member types (4.4 expr.qual), the cv-compatible type of T1 and T2;
- otherwise, a program that necessitates the determination of a composite pointer type is ill-formed.
typedef void *p; typedef const int *q; typedef int **pi; typedef const int **pci;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". ]
Change in 5.9 expr.rel paragraphs 1 to 5:
Change in 5.10 expr.eq paragraphs 1 to 4:... The operands shall have arithmetic, enumeration, or pointer type
, or type std::nullptr_t.The usual arithmetic conversions are performed on operands of arithmetic or enumeration type. If both operands are pointers, pointer
Pointerconversions (4.10 conv.ptr) and qualification conversions (4.4 conv.qual) are performedon 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)to bring them to theircomposite pointer typecomposite pointer type (clause 5 expr).If one operand is a null pointer constant, the composite pointer type isstd::nullptr_t
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: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 }-- end example ]After conversions, the operands shall have the same type.
Pointers to objects or functions of the same type (after pointer conversions) can be compared, with a resultComparing pointers to objects is defined as follows:If two operands p and q compare equal (5.10 expr.eq), p<=q and p>=q both yield
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<=q and p>=q both yield true and p<q and p>q both yield false.If two pointers p and qof the same typepoint 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<q, p>q, p<=q, and p>=q are unspecified.- 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 greater.
- If two pointers point to non-static data members of the same object, or to subobjects or array elements 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.
If two pointers point to non-static data members of the same object with different access control (Clause 11) the result is unspecified.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.Other pointer comparisons are unspecified.true
and p<q and p>q both yieldfalse
. Otherwise, if a pointer p compares greater than a pointer q, p>=q and p>q both yieldtrue
and p<=q and p<q both yieldfalse
. Otherwise, the result of each of the operators is unspecified.
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 <= or >= and false otherwise; otherwise the result is unspecified.
If two operands of typestd::nullptr_t
are compared, the result is true if the operator is <= or >=, and false otherwise.If both operands (after conversions) are of arithmetic or enumeration type, each of the operators shall yield
true
if the specified relationship is true andfalse
if it is false.
Change in 5.16 expr.cond paragraph 6:The == (equal to) and the != (not equal to) operators group left-to-right. The operands shall have arithmetic, enumeration, pointer, or pointer to member type, or type
std::nullptr_t
. The operators == and != both yieldtrue
orfalse
, i.e. a result of typebool
.have the same semantic restrictions, conversions, and result type as the relational operators except for their lower precedence and truth-value result. [ Note: a<b == c<d is true whenever a<b and c<d have the same truth-value. -- end note ]In each case below, the operands shall have the same type after the specified conversions have been applied.If 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:
Pointers of the same type (after pointer conversions) can be compared for equality.Two pointersof the same typecompare equal ifand only ifthey are both null, both point to the same function, or both represent the same address (3.9.2 basic.compound), otherwise they compare unequal.If one of the operands is a pointer to member, pointer
In addition, pointers to members can be compared, or a pointer to member and a null pointer constant. Pointerto member conversions (4.11 conv.mem) and qualification conversions (4.4 conv.qual) are performed on both operands to bring them toa common typetheir composite pointer type (clause 5 expr).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 ]Comparing pointers to member is defined as follows:
- If
both operandstwo pointers to member are both the null member pointer value, they compare equal.Otherwise ifIf only one of two pointers to member is the null member pointer value, they compare unequal.Otherwise ifIf either is a pointer to a virtual member function, the result is unspecified.Otherwise theyTwo pointers to member compare equal ifand only ifthey would refer to the same member function of the same most derived object (1.8 intro.object) or to the same subobject if they were dereferenced with a hypothetical object of the associated class type, otherwise they compare unequal. [ Example:struct B { int f(); }; struct L : B { }; struct R : B { }; struct D : L, R { }; int (B::*pb)() = &B::f; int (L::*pl)() = pb; int (R::*pr)() = pb; int (D::*pdl)() = pl; int (D::*pdr)() = pr; bool x = (pdl == pdr); // false-- end example ]If twoTwo operands of typestd::nullptr_t
or one operand of typestd::nullptr_t
and the other a null pointer constant compare equalare compared, the result is true if the operator is ==, and false otherwise.If two operands compare equal, the result is
true
for operator== andfalse
for operator!=. If two operands compare unequal, the result isfalse
for operator== andtrue
for operator!=. Otherwise, the result of each of the operators is unspecified.If both operands are of arithmetic or enumeration type, the usual arithmetic conversions are performed on both operands; each
Eachof the operators shall yield true if the specified relationship is true and false if it is false. [ Note: a<b == c<d is true whenever a<b and c<d have the same truth-value. -- end note ]
Change in 9.5 class.union paragraph 1:
- ...
- One or both of the
Thesecond and third operands have pointer type, 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; pointer conversions (4.10 conv.ptr) and qualification conversions (4.4 conv.qual) are performed to bring them to their composite pointer type(5.9 expr.rel)(5 expr). The result is of the composite pointer type.- One or both of the
Thesecond and third operands have pointer to member type, or one has pointer to member type and the other is a null pointer constant; pointer to member conversions (4.11 conv.mem) and qualification conversions (4.4 conv.qual) are performed to bring them toa common type, whose cv-qualification shall match the cv-qualification of either the second or the third operandtheir composite pointer type (5 expr). The result is of thecommoncomposite pointer type.- Both the second and third operand have type
std::nullptr_t
or one has that type and the other is a null pointer constant. The result is a of typestd::nullptr_t
.
... Each non-static data member is allocated as if it were the sole member of a struct. All non-static data members of a union object have the same address. ...Change in 13.6 over.built paragraphs 15 and 16:
For every T , where T is an enumeration typeChange in 20.7.2.2.7 util.smartptr.shared.cmp paragraph 2:,or a pointer type, or, there exist candidate operator functions of the formstd::nullptr_t
bool operator<(T , T ); bool operator>(T , T ); bool operator<=(T , T ); bool operator>=(T , T ); bool operator==(T , T ); bool operator!=(T , T );For every pointer to member type T or typestd::nullptr_t
there exist candidate operator functions of the formbool operator==(T , T ); bool operator!=(T , T );
Returns:less<V>()(a.get(), b.get())
, where V is the composite pointer type(5.9 expr.rel)(5 expr) of T* and U*.