Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 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?

like image 267
Matteo Italia Avatar asked Jan 25 '19 09:01

Matteo Italia


People also ask

Can you compare void pointers in C?

@jl987: It is not possible to compare values pointed by void * pointers without expilcitly telling the compiler how to compare them. The compiler does not know it. Only you know how to do that. In the simplest case you will have to tell the compiler what types these values have by specifying the exact type in the cast.

What is the purpose of void pointer in C?

The void pointer in C is a pointer that is not associated with any data types. It points to some data location in the storage. This means that it points to the address of variables. It is also called the general purpose pointer.

Why arithmetic operation Cannot be perform on a void pointer?

Since void is an incomplete type, it is not an object type. Therefore it is not a valid operand to an addition operation. Therefore you cannot perform pointer arithmetic on a void pointer.

How do you compare two voids?

You can legally compare a void pointer with any other pointer using the “==” and “!=


1 Answers

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

  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.

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.


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.

like image 169
P.W Avatar answered Oct 02 '22 07:10

P.W