C++ Logo

std-discussion

Advanced search

std::less, tuple, pointer ordering

From: Giuseppe D'Angelo <giuseppe.dangelo_at_[hidden]>
Date: Tue, 30 Jun 2020 04:21:10 +0200
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:24:29