Date: Tue, 16 Sep 2025 17:58:23 -0400
> One common use case is testing whether the pointers point to a subobject
of the same object. For example, if a pointer points within a string or
an array. This check is often needed to properly implement operations on
the string/array, such as selecting the direction of element iteration.
This case is already well defined in the standard: the last declared member has higher address than the one declared before (unless one has zero size).
One important point to mention: the standard guarantee that the < relationship is a partial order, so even though the result of 'a < b' may be unspecified, if 'a < b' does evaluate to true then it follows that 'b <= a' evaluates to false.
-------- Original Message --------
From: Andrey Semashev via Std-Discussion <std-discussion_at_[hidden]>
Sent: September 16, 2025 6:33:44 a.m. EDT
To: std-discussion_at_[hidden]
Cc: Andrey Semashev <andrey.semashev_at_[hidden]>
Subject: Re: [std-discussion] Pointer comparison with operators vs function objects
On 15 Sep 2025 16:48, David Brown via Std-Discussion wrote:
>
> First ask yourself, is there any reason why you would want to compare
> unrelated pointers for ordering?
One common use case is testing whether the pointers point to a subobject
of the same object. For example, if a pointer points within a string or
an array. This check is often needed to properly implement operations on
the string/array, such as selecting the direction of element iteration.
> Such comparisons can be helpful for implementations of standard
> functions like memmove(). But those are part of the implementation - a
> standard library implementation can use compiler-specific features or
> implementation-specific knowledge to implement its functions
> efficiently, relying on features that are not part of the C++ standards.
Memmove is not always applicable, e.g. when the elements are not
trivially copyable. And testing as part of the copy operation may not be
enough. Consider e.g. a call to std::string::insert, where the inserted
string may part of the target string. You would have to test whether the
inserted string is part of the target string before you perform the copy
because that will affect how you perform the copy.
> There might be occasions when user-written code can have an interest in
> orderings of pointers, such as for writing specialised memory management
> routines or perhaps holding unrelated pointers in a searchable data
> structure. I would expect that for those situations, you can simple
> cast to uintptr_t and compare those without any overhead. (Use the
> casts just for the comparisons - avoid casting back and forth between
> uintptr_t and pointer types because the compiler might lose useful
> information.) You will probably find that this is what is being the
> std::less implementation in many C++ libraries.
This misses the rationale why the casts are needed in the first place.
> As for why comparing unrelated pointers (except for equality) is UB,
> this might be because C and C++ can work on systems with complicated
> address spaces. Modern x86, ARM, and other common "big" processors have
> nice, simple flat address spaces. But some other targets have
> complicated address spaces - the same underlying representation in a
> pointer might refer to different physical addresses depending on its
> type or how the pointer is used. (An example of a currently available
> processor with multiple address spaces and with C++ support is the AVR
> microcontroller family.)
And on such systems casts to uintptr_t would make the comparisons
invalid, right?
> On many such systems, it would be possible to
> define a comparison relationship - but why bother, if the results don't
> actually mean anything?
The meaning would be the same as std::less. Specifically, the unrelated
pointers are unordered (meaning, unrelated pointers compare
consistently, but possibly differently on every run of the program) and
pointers within the object compare as if the pointers were offsets from
the beginning of the object representation.
Really, the language already defines the logic under the name of
std::less. Why the same logic cannot be invoked as the built-in
operrator< for pointers?
of the same object. For example, if a pointer points within a string or
an array. This check is often needed to properly implement operations on
the string/array, such as selecting the direction of element iteration.
This case is already well defined in the standard: the last declared member has higher address than the one declared before (unless one has zero size).
One important point to mention: the standard guarantee that the < relationship is a partial order, so even though the result of 'a < b' may be unspecified, if 'a < b' does evaluate to true then it follows that 'b <= a' evaluates to false.
-------- Original Message --------
From: Andrey Semashev via Std-Discussion <std-discussion_at_[hidden]>
Sent: September 16, 2025 6:33:44 a.m. EDT
To: std-discussion_at_[hidden]
Cc: Andrey Semashev <andrey.semashev_at_[hidden]>
Subject: Re: [std-discussion] Pointer comparison with operators vs function objects
On 15 Sep 2025 16:48, David Brown via Std-Discussion wrote:
>
> First ask yourself, is there any reason why you would want to compare
> unrelated pointers for ordering?
One common use case is testing whether the pointers point to a subobject
of the same object. For example, if a pointer points within a string or
an array. This check is often needed to properly implement operations on
the string/array, such as selecting the direction of element iteration.
> Such comparisons can be helpful for implementations of standard
> functions like memmove(). But those are part of the implementation - a
> standard library implementation can use compiler-specific features or
> implementation-specific knowledge to implement its functions
> efficiently, relying on features that are not part of the C++ standards.
Memmove is not always applicable, e.g. when the elements are not
trivially copyable. And testing as part of the copy operation may not be
enough. Consider e.g. a call to std::string::insert, where the inserted
string may part of the target string. You would have to test whether the
inserted string is part of the target string before you perform the copy
because that will affect how you perform the copy.
> There might be occasions when user-written code can have an interest in
> orderings of pointers, such as for writing specialised memory management
> routines or perhaps holding unrelated pointers in a searchable data
> structure. I would expect that for those situations, you can simple
> cast to uintptr_t and compare those without any overhead. (Use the
> casts just for the comparisons - avoid casting back and forth between
> uintptr_t and pointer types because the compiler might lose useful
> information.) You will probably find that this is what is being the
> std::less implementation in many C++ libraries.
This misses the rationale why the casts are needed in the first place.
> As for why comparing unrelated pointers (except for equality) is UB,
> this might be because C and C++ can work on systems with complicated
> address spaces. Modern x86, ARM, and other common "big" processors have
> nice, simple flat address spaces. But some other targets have
> complicated address spaces - the same underlying representation in a
> pointer might refer to different physical addresses depending on its
> type or how the pointer is used. (An example of a currently available
> processor with multiple address spaces and with C++ support is the AVR
> microcontroller family.)
And on such systems casts to uintptr_t would make the comparisons
invalid, right?
> On many such systems, it would be possible to
> define a comparison relationship - but why bother, if the results don't
> actually mean anything?
The meaning would be the same as std::less. Specifically, the unrelated
pointers are unordered (meaning, unrelated pointers compare
consistently, but possibly differently on every run of the program) and
pointers within the object compare as if the pointers were offsets from
the beginning of the object representation.
Really, the language already defines the logic under the name of
std::less. Why the same logic cannot be invoked as the built-in
operrator< for pointers?
-- Std-Discussion mailing list Std-Discussion_at_[hidden] https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion -- Julien Villemure Sent from my Android device with K-9 Mail.
Received on 2025-09-16 21:58:35