Why does C++11 contain an odd clause about comparing void pointers?

  • A+
Category:Languages

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 is true if the operator is <= or >= and false 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?

 


This is related to the Core Issue 583: Relational pointer comparisons against the null pointer constant

  1. 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.

AND Core Issue 1512: Pointer comparison vs qualification conversions

  1. 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 is true if the operator is <= or >= and false 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.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: