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?
c++ c++11 pointers language-lawyer comparison-operators
|
show 3 more comments
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?
c++ c++11 pointers language-lawyer comparison-operators
It's not odd, C and C++ are typed languages, assigning the address of an integer array to void* isn't a like for like assignment therefore a cast is required. static_cast<void*>(foo);
– SPlatten
Jan 25 at 9:23
3
@SPlatten: any data pointer has an implicit conversion tovoid *
, so no explicit cast is required, although it's true that on some odd architectures (segmented memory comes to mind) casting tovoid *
may not be a plain bitwise copy; still, I cannot imagine an architecture where the "regular pointer" to "big,void
pointer" conversion wouldn't preserve the ordering relation between elements of the same array.
– Matteo Italia
Jan 25 at 9:28
I could image pointer comparison being implemented as "count the number of elements between a and b; if it's negative then b<a". That won't work forvoid*
of course, as there's novoid
.
– MSalters
Jan 25 at 9:47
3
@MSalters: I can also imaginea - b
being implemented asint c; while((c = rand()) + b == a);
, but that doesn't mean it's anywhere near sensible. :-) Also, I'd expect it to violate the time complexity requirements stated somewhere in the algorithms/containers section.
– Matteo Italia
Jan 25 at 9:52
According to the wording of that clausechar c; bool b = (void *)&c == (void *)&c
would be false, against all reason. Am I reading it correctly?
– Peter A. Schneider
Jan 25 at 14:13
|
show 3 more comments
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?
c++ c++11 pointers language-lawyer comparison-operators
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?
c++ c++11 pointers language-lawyer comparison-operators
c++ c++11 pointers language-lawyer comparison-operators
asked Jan 25 at 9:20
Matteo ItaliaMatteo Italia
101k15147245
101k15147245
It's not odd, C and C++ are typed languages, assigning the address of an integer array to void* isn't a like for like assignment therefore a cast is required. static_cast<void*>(foo);
– SPlatten
Jan 25 at 9:23
3
@SPlatten: any data pointer has an implicit conversion tovoid *
, so no explicit cast is required, although it's true that on some odd architectures (segmented memory comes to mind) casting tovoid *
may not be a plain bitwise copy; still, I cannot imagine an architecture where the "regular pointer" to "big,void
pointer" conversion wouldn't preserve the ordering relation between elements of the same array.
– Matteo Italia
Jan 25 at 9:28
I could image pointer comparison being implemented as "count the number of elements between a and b; if it's negative then b<a". That won't work forvoid*
of course, as there's novoid
.
– MSalters
Jan 25 at 9:47
3
@MSalters: I can also imaginea - b
being implemented asint c; while((c = rand()) + b == a);
, but that doesn't mean it's anywhere near sensible. :-) Also, I'd expect it to violate the time complexity requirements stated somewhere in the algorithms/containers section.
– Matteo Italia
Jan 25 at 9:52
According to the wording of that clausechar c; bool b = (void *)&c == (void *)&c
would be false, against all reason. Am I reading it correctly?
– Peter A. Schneider
Jan 25 at 14:13
|
show 3 more comments
It's not odd, C and C++ are typed languages, assigning the address of an integer array to void* isn't a like for like assignment therefore a cast is required. static_cast<void*>(foo);
– SPlatten
Jan 25 at 9:23
3
@SPlatten: any data pointer has an implicit conversion tovoid *
, so no explicit cast is required, although it's true that on some odd architectures (segmented memory comes to mind) casting tovoid *
may not be a plain bitwise copy; still, I cannot imagine an architecture where the "regular pointer" to "big,void
pointer" conversion wouldn't preserve the ordering relation between elements of the same array.
– Matteo Italia
Jan 25 at 9:28
I could image pointer comparison being implemented as "count the number of elements between a and b; if it's negative then b<a". That won't work forvoid*
of course, as there's novoid
.
– MSalters
Jan 25 at 9:47
3
@MSalters: I can also imaginea - b
being implemented asint c; while((c = rand()) + b == a);
, but that doesn't mean it's anywhere near sensible. :-) Also, I'd expect it to violate the time complexity requirements stated somewhere in the algorithms/containers section.
– Matteo Italia
Jan 25 at 9:52
According to the wording of that clausechar c; bool b = (void *)&c == (void *)&c
would be false, against all reason. Am I reading it correctly?
– Peter A. Schneider
Jan 25 at 14:13
It's not odd, C and C++ are typed languages, assigning the address of an integer array to void* isn't a like for like assignment therefore a cast is required. static_cast<void*>(foo);
– SPlatten
Jan 25 at 9:23
It's not odd, C and C++ are typed languages, assigning the address of an integer array to void* isn't a like for like assignment therefore a cast is required. static_cast<void*>(foo);
– SPlatten
Jan 25 at 9:23
3
3
@SPlatten: any data pointer has an implicit conversion to
void *
, so no explicit cast is required, although it's true that on some odd architectures (segmented memory comes to mind) casting to void *
may not be a plain bitwise copy; still, I cannot imagine an architecture where the "regular pointer" to "big, void
pointer" conversion wouldn't preserve the ordering relation between elements of the same array.– Matteo Italia
Jan 25 at 9:28
@SPlatten: any data pointer has an implicit conversion to
void *
, so no explicit cast is required, although it's true that on some odd architectures (segmented memory comes to mind) casting to void *
may not be a plain bitwise copy; still, I cannot imagine an architecture where the "regular pointer" to "big, void
pointer" conversion wouldn't preserve the ordering relation between elements of the same array.– Matteo Italia
Jan 25 at 9:28
I could image pointer comparison being implemented as "count the number of elements between a and b; if it's negative then b<a". That won't work for
void*
of course, as there's no void
.– MSalters
Jan 25 at 9:47
I could image pointer comparison being implemented as "count the number of elements between a and b; if it's negative then b<a". That won't work for
void*
of course, as there's no void
.– MSalters
Jan 25 at 9:47
3
3
@MSalters: I can also imagine
a - b
being implemented as int c; while((c = rand()) + b == a);
, but that doesn't mean it's anywhere near sensible. :-) Also, I'd expect it to violate the time complexity requirements stated somewhere in the algorithms/containers section.– Matteo Italia
Jan 25 at 9:52
@MSalters: I can also imagine
a - b
being implemented as int c; while((c = rand()) + b == a);
, but that doesn't mean it's anywhere near sensible. :-) Also, I'd expect it to violate the time complexity requirements stated somewhere in the algorithms/containers section.– Matteo Italia
Jan 25 at 9:52
According to the wording of that clause
char c; bool b = (void *)&c == (void *)&c
would be false, against all reason. Am I reading it correctly?– Peter A. Schneider
Jan 25 at 14:13
According to the wording of that clause
char c; bool b = (void *)&c == (void *)&c
would be false, against all reason. Am I reading it correctly?– Peter A. Schneider
Jan 25 at 14:13
|
show 3 more comments
1 Answer
1
active
oldest
votes
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.
3
This explains why it was removed, but why was it added in first place? What was the committee trying to achieve by restricting the comparison ofvoid *
? Or I am mistaken and the example in my question results in unspecified behavior on current-day C++ (IOW they just changed the wording)?
– Matteo Italia
Jan 25 at 13:44
1
@MatteoItalia: I tried to hunt for the reason why it was added by looking up the "Compatibility with C++03" section in the C++11 standard but could not find anything related mentioned. Current day C++(17) also does not mention this clause in any form (as far as I could check the standard), so the behavior may just be unspecified.
– P.W
Jan 25 at 13:56
2
The "oddball clause" was unrelated to either core issue. It's more of a drive-by fix as the issue resolution rewrote the subclause.
– T.C.
Jan 25 at 18:16
@MatteoItalia: Please see edit to the answer which answers the question why this odd clause was added in the first place.
– P.W
Jan 28 at 5:20
1
@MatteoItalia: The flow looks more structured now. Thanks for the edit.
– P.W
Jan 30 at 4:58
|
show 9 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54362209%2fwhy-does-c11-contain-an-odd-clause-about-comparing-void-pointers%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
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.
3
This explains why it was removed, but why was it added in first place? What was the committee trying to achieve by restricting the comparison ofvoid *
? Or I am mistaken and the example in my question results in unspecified behavior on current-day C++ (IOW they just changed the wording)?
– Matteo Italia
Jan 25 at 13:44
1
@MatteoItalia: I tried to hunt for the reason why it was added by looking up the "Compatibility with C++03" section in the C++11 standard but could not find anything related mentioned. Current day C++(17) also does not mention this clause in any form (as far as I could check the standard), so the behavior may just be unspecified.
– P.W
Jan 25 at 13:56
2
The "oddball clause" was unrelated to either core issue. It's more of a drive-by fix as the issue resolution rewrote the subclause.
– T.C.
Jan 25 at 18:16
@MatteoItalia: Please see edit to the answer which answers the question why this odd clause was added in the first place.
– P.W
Jan 28 at 5:20
1
@MatteoItalia: The flow looks more structured now. Thanks for the edit.
– P.W
Jan 30 at 4:58
|
show 9 more comments
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.
3
This explains why it was removed, but why was it added in first place? What was the committee trying to achieve by restricting the comparison ofvoid *
? Or I am mistaken and the example in my question results in unspecified behavior on current-day C++ (IOW they just changed the wording)?
– Matteo Italia
Jan 25 at 13:44
1
@MatteoItalia: I tried to hunt for the reason why it was added by looking up the "Compatibility with C++03" section in the C++11 standard but could not find anything related mentioned. Current day C++(17) also does not mention this clause in any form (as far as I could check the standard), so the behavior may just be unspecified.
– P.W
Jan 25 at 13:56
2
The "oddball clause" was unrelated to either core issue. It's more of a drive-by fix as the issue resolution rewrote the subclause.
– T.C.
Jan 25 at 18:16
@MatteoItalia: Please see edit to the answer which answers the question why this odd clause was added in the first place.
– P.W
Jan 28 at 5:20
1
@MatteoItalia: The flow looks more structured now. Thanks for the edit.
– P.W
Jan 30 at 4:58
|
show 9 more comments
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.
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.
edited Jan 29 at 21:44
Matteo Italia
101k15147245
101k15147245
answered Jan 25 at 9:47
P.WP.W
14.2k31248
14.2k31248
3
This explains why it was removed, but why was it added in first place? What was the committee trying to achieve by restricting the comparison ofvoid *
? Or I am mistaken and the example in my question results in unspecified behavior on current-day C++ (IOW they just changed the wording)?
– Matteo Italia
Jan 25 at 13:44
1
@MatteoItalia: I tried to hunt for the reason why it was added by looking up the "Compatibility with C++03" section in the C++11 standard but could not find anything related mentioned. Current day C++(17) also does not mention this clause in any form (as far as I could check the standard), so the behavior may just be unspecified.
– P.W
Jan 25 at 13:56
2
The "oddball clause" was unrelated to either core issue. It's more of a drive-by fix as the issue resolution rewrote the subclause.
– T.C.
Jan 25 at 18:16
@MatteoItalia: Please see edit to the answer which answers the question why this odd clause was added in the first place.
– P.W
Jan 28 at 5:20
1
@MatteoItalia: The flow looks more structured now. Thanks for the edit.
– P.W
Jan 30 at 4:58
|
show 9 more comments
3
This explains why it was removed, but why was it added in first place? What was the committee trying to achieve by restricting the comparison ofvoid *
? Or I am mistaken and the example in my question results in unspecified behavior on current-day C++ (IOW they just changed the wording)?
– Matteo Italia
Jan 25 at 13:44
1
@MatteoItalia: I tried to hunt for the reason why it was added by looking up the "Compatibility with C++03" section in the C++11 standard but could not find anything related mentioned. Current day C++(17) also does not mention this clause in any form (as far as I could check the standard), so the behavior may just be unspecified.
– P.W
Jan 25 at 13:56
2
The "oddball clause" was unrelated to either core issue. It's more of a drive-by fix as the issue resolution rewrote the subclause.
– T.C.
Jan 25 at 18:16
@MatteoItalia: Please see edit to the answer which answers the question why this odd clause was added in the first place.
– P.W
Jan 28 at 5:20
1
@MatteoItalia: The flow looks more structured now. Thanks for the edit.
– P.W
Jan 30 at 4:58
3
3
This explains why it was removed, but why was it added in first place? What was the committee trying to achieve by restricting the comparison of
void *
? Or I am mistaken and the example in my question results in unspecified behavior on current-day C++ (IOW they just changed the wording)?– Matteo Italia
Jan 25 at 13:44
This explains why it was removed, but why was it added in first place? What was the committee trying to achieve by restricting the comparison of
void *
? Or I am mistaken and the example in my question results in unspecified behavior on current-day C++ (IOW they just changed the wording)?– Matteo Italia
Jan 25 at 13:44
1
1
@MatteoItalia: I tried to hunt for the reason why it was added by looking up the "Compatibility with C++03" section in the C++11 standard but could not find anything related mentioned. Current day C++(17) also does not mention this clause in any form (as far as I could check the standard), so the behavior may just be unspecified.
– P.W
Jan 25 at 13:56
@MatteoItalia: I tried to hunt for the reason why it was added by looking up the "Compatibility with C++03" section in the C++11 standard but could not find anything related mentioned. Current day C++(17) also does not mention this clause in any form (as far as I could check the standard), so the behavior may just be unspecified.
– P.W
Jan 25 at 13:56
2
2
The "oddball clause" was unrelated to either core issue. It's more of a drive-by fix as the issue resolution rewrote the subclause.
– T.C.
Jan 25 at 18:16
The "oddball clause" was unrelated to either core issue. It's more of a drive-by fix as the issue resolution rewrote the subclause.
– T.C.
Jan 25 at 18:16
@MatteoItalia: Please see edit to the answer which answers the question why this odd clause was added in the first place.
– P.W
Jan 28 at 5:20
@MatteoItalia: Please see edit to the answer which answers the question why this odd clause was added in the first place.
– P.W
Jan 28 at 5:20
1
1
@MatteoItalia: The flow looks more structured now. Thanks for the edit.
– P.W
Jan 30 at 4:58
@MatteoItalia: The flow looks more structured now. Thanks for the edit.
– P.W
Jan 30 at 4:58
|
show 9 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54362209%2fwhy-does-c11-contain-an-odd-clause-about-comparing-void-pointers%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
It's not odd, C and C++ are typed languages, assigning the address of an integer array to void* isn't a like for like assignment therefore a cast is required. static_cast<void*>(foo);
– SPlatten
Jan 25 at 9:23
3
@SPlatten: any data pointer has an implicit conversion to
void *
, so no explicit cast is required, although it's true that on some odd architectures (segmented memory comes to mind) casting tovoid *
may not be a plain bitwise copy; still, I cannot imagine an architecture where the "regular pointer" to "big,void
pointer" conversion wouldn't preserve the ordering relation between elements of the same array.– Matteo Italia
Jan 25 at 9:28
I could image pointer comparison being implemented as "count the number of elements between a and b; if it's negative then b<a". That won't work for
void*
of course, as there's novoid
.– MSalters
Jan 25 at 9:47
3
@MSalters: I can also imagine
a - b
being implemented asint c; while((c = rand()) + b == a);
, but that doesn't mean it's anywhere near sensible. :-) Also, I'd expect it to violate the time complexity requirements stated somewhere in the algorithms/containers section.– Matteo Italia
Jan 25 at 9:52
According to the wording of that clause
char c; bool b = (void *)&c == (void *)&c
would be false, against all reason. Am I reading it correctly?– Peter A. Schneider
Jan 25 at 14:13