C++ Logo

std-discussion

Advanced search

Re: std::less, tuple, pointer ordering

From: Tony V E <tvaneerd_at_[hidden]>
Date: Mon, 29 Jun 2020 22:29:32 -0400
My thoughts have always been that tuple, optional, etc should implement less to call less on their elements. 

The standard should do the extra effort to do it right. But I expect less effort for user code. 

For users, I'm fine with their custom wrappers calling < on pointers, because it is implementation defined, and fine. The code you posted is probably defined on all platforms that Qt supports.

Sent from my BlackBerry portable Babbage Device
  Original Message  
From: Giuseppe D'Angelo via Std-Discussion
Sent: Monday, June 29, 2020 10:21 PM
To: std-discussion_at_[hidden]
Reply To: std-discussion_at_[hidden]
Cc: Giuseppe D'Angelo
Subject: [std-discussion] std::less, tuple, pointer ordering

Hi,

Today I stumbled across the following piece of code (simplified):

class QModelIndex {
QModel *model;
int row, column;
intptr data;

auto salient_members() const {
return std::tie(model, row, column, data);
}

friend bool operator==(QModelIndex lhs, QModelIndex rhs) {
return lhs.salient_members() == rhs.salient_members();
}

friend bool operator<(QModelIndex lhs, QModelIndex rhs) {
return lhs.salient_members() < rhs.salient_members();
}
};

operator<, as implemented, depends on unspecified behavior: it will use
tuple's operator<, which in turn uses operator< on the "model" element
in the tuple, which leads to unspecified behavior in the general case of
unrelated object pointers [expr.rel§4.3].

(The "unspecified" seems to have been removed after C++14, now the
wording says "neither pointer is required to compare greater than the
other", which still sounds pretty unspecified to me. Side note,
[expr.spaceship§7] has identical semantics -- unspecified behavior --
for object pointers, so having it synthesize operator< for the class
above would also be "erroneous".)


When comparing unrelated object pointers, one shall use std::less, but
the problem stays the same; using

std::less{}(lhs.salient_members(), rhs.salient_members())

will not help here, as it will simply call operator< on the tuple.

The point being: std::less does do something "magic" like a
lexicographical comparison applying itself, in turn, to each
corresponding element of the two tuples.

For the same reason, generalizing, something like

std::set<std::tuple<~~~, pointer, ~~~>>

is also broken.


So, should std::less (& co.) have such "magical unwrapping" behavior?

The big problem with this idea is that it's not specific to tuple. In
other words, it opens an endless tunnel: one could similarly reason that
any container-like object (pair, variant, optional, vector, array...)
should also be similarly unwrapped and std::less applied to its contents...

To me, this doesn't sound very appealing: it's not scalable (every
container-like that defines operator< should also specialize less); it
would obviously be a behavioral change; it would come with a teaching
burden; etc.


Has anyone given the matter any thoughts already?


Thanks for reading,
-- 
Giuseppe D'Angelo | giuseppe.dangelo_at_[hidden] | Senior Software Engineer
KDAB (France) S.A.S., a KDAB Group company
Tel. France +33 (0)4 90 84 08 53, http://www.kdab.com
KDAB - The Qt, C++ and OpenGL Experts

Received on 2020-06-29 21:32:47