C++ Logo


Advanced search

Subject: Re: std::less, tuple, pointer ordering
From: Tony V E (tvaneerd_at_[hidden])
Date: 2020-06-29 21:29:32

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


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

STD-DISCUSSION list run by herb.sutter at gmail.com

Older Archives on Google Groups