C++ Logo

std-discussion

Advanced search

Re: Pointer comparison with operators vs function objects

From: David Brown <david.brown_at_[hidden]>
Date: Mon, 15 Sep 2025 18:32:04 +0200
On 15/09/2025 16:54, Thiago Macieira via Std-Discussion wrote:
> On Monday, 15 September 2025 06:59:34 Pacific Daylight Time Nate Eldredge via
> Std-Discussion wrote:
>> Cost. std::less may be more expensive than < , and the programmer should
>> not have to pay that cost for the common case of comparing pointers within
>> the same array.
>
> There may be a higher cost to compile, but runtime the cost should be exactly
> the same.
>
>> The classic example was x86-16 with the "compact" or "large" code models, in
>> which a data pointer is a "far" pointer consisting roughly of struct {
>> unsigned offset; unsigned segment; };. (Here `unsigned` is 16 bits.) A
>> 20-bit linear address is formed by (unsigned long)segment << 4 + offset.
>>
>> In these code models, every individual array exists within a single segment
>> (and so is limited to 65535 bytes) but distinct arrays may be in different
>> segments. So p1 < p2 can simply compare the offset parts, because the
>> segments must agree if they point within the same array, which costs one
>> cheap 16-bit compare instruction (cmp si, di). But std::less<T *>{}(p1,
>> p2) must compute and compare the linear addresses, requiring several shifts
>> and 32-bit adds, and being about an order of magnitude slower and larger.
>
> Indeed, but in the medium and large memory models (__far function pointers),
> you'd need to do the above anyway to compare function pointers, because you
> couldn't be sure whether they were in the same segment or not. For example,
> how do you order 0010:0108 vs 0200:0010 ?

Function pointer comparison and data pointer comparison are, AFIUI,
different operations in standards terms - because you can't necessarily
convert between function and data pointer types - I believe you can only
do so using reinterpret_cast, and support is conditional and
implementation defined. So an implementation could use segment-aware
far pointer comparisons for function pointers and segment unaware near
pointer comparisons for data pointers.

>
> Besides, std::less<T __far *> can be different from std::less<T __near *>.
>

Presumably that is the case for implementations that have __far and
__near, but those are not standard. From the standard viewpoint, if
these are different kinds of pointers, they should be uncompareable by
normal relational operations (i.e., the operation is UB). std::less, on
the other hand, has to provide a bool answer - or the implementation can
choose to throw an exception. (I'm basing that on cppreference.com - I
haven't read the standards details here.)

As far as I can tell, this all means that it is valid for a compiler to
use 16-bit comparisons for "near" pointers on segmented x86 when you
write "p < q", but it would have to do a segment-aware 32-bit comparison
for std::less(p, q) on the same architecture. Similar requirements
would apply to other targets with multiple address spaces (like the AVR
- though since avr-gcc does not appear to support <functional>, it is
difficult to test!).

Received on 2025-09-15 16:32:12