Why does C++11 contain an odd clause about comparing void pointers?
While checking the references for another question, I noticed an odd clause in C++11, at [expr.rel] ¶3:
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 istrue
if the operator is<=
or>=
andfalse
otherwise; otherwise the result is unspecified.
This seems to mean that, once two pointers have been casted to void *
, their ordering relation is no longer guaranteed; for example, this:
int foo[] = {1, 2, 3, 4, 5};
void *a = &foo[0];
void *b = &foo[1];
std::cout<<(a < b);
would seem to be unspecified.
Interestingly, this clause wasn't there in C++03 and disappeared in C++14, so if we take the example above and apply the C++14 wording to it, I'd say that ¶3.1
- 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.
would apply, as a
and b
point to elements of the same array, even though they have been casted to void *
. Notice that the wording of ¶3.1 was there pretty much the same in C++11, but seemed to be overridden by the void *
clause.
Am I right in my understanding? What was the point of that oddball clause added in C++11 and immediately removed? Or maybe it's still there, but moved to/implied by some other part of the standard?
Solution 1:
TL;DR:
- in C++98/03 the clause was not present, and the standard did not specify relational operators for
void
pointers (core issue 879, see end of this post); - the odd clause about comparing
void
pointers was added in C++11 to resolve it, but this in turn gave rise to two other core issues 583 & 1512 (see below); - the resolution of these issues required the clause to be removed and be replaced with the wording found in C++14 standard, which allows for "normal"
void *
comparison.
Core Issue 583: Relational pointer comparisons against the null pointer constant
- Relational pointer comparisons against the null pointer constant Section: 8.9 [expr.rel]
In C, this is ill-formed (cf C99 6.5.8):
void f(char* s) { if (s < 0) { } } ...but in C++, it's not. Why? Who would ever need to write (s > 0) when they could just as well write (s != 0)?
This has been in the language since the ARM (and possibly earlier); apparently it's because the pointer conversions (7.11 [conv.ptr]) need to be performed on both operands whenever one of the operands is of pointer type. So it looks like the "null-ptr-to-real-pointer-type" conversion is hitching a ride with the other pointer conversions.
Proposed resolution (April, 2013):
This issue is resolved by the resolution of issue 1512.
Core Issue 1512: Pointer comparison vs qualification conversions
- Pointer comparison vs qualification conversions Section: 8.9 [expr.rel]
According to 8.9 [expr.rel] paragraph 2, describing pointer comparisons,
Pointer conversions (7.11 [conv.ptr]) and qualification conversions (7.5 [conv.qual]) are performed 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) to bring them to their composite pointer type. This would appear to make the following example ill-formed,
bool foo(int** x, const int** y) { return x < y; // valid ? } because int** cannot be converted to const int**, according to the rules of 7.5 [conv.qual] paragraph 4.
This seems too strict for pointer comparison, and current implementations accept the example.
Proposed resolution (November, 2012):
Relevant excerpts from resolution of the above issues are found in the paper: Pointer comparison vs qualification conversions (revision 3).
The following also resolves core issue 583.
Change in 5.9 expr.rel paragraphs 1 to 5:
In this section the following statement (the odd clause in C++11) has been expunged:
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 istrue
if the operator is<=
or>=
andfalse
otherwise; otherwise the result is unspecified
And the following statements have been added:
- 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.
- 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.
So in the final working draft of C++14 (n4140) section [expr.rel]/3, the above statements are found as they were stated at the time of the resolution.
Digging for the reason why this odd clause was added led me to a much earlier issue 879: Missing built-in comparison operators for pointer types. The proposed resolution of this issue (in July, 2009) led to the addition of this clause which was voted into WP in October, 2009.
And that is how it came to be included in the C++11 standard.