Boost logo

Boost :

Subject: Re: [boost] [operators] A modern SFINAE-based version of boost::operators?
From: Matt Calabrese (rivorus_at_[hidden])
Date: 2017-11-15 20:26:08


On Mon, Nov 13, 2017 at 9:20 AM, Beman Dawes via Boost
<boost_at_[hidden]> wrote:
> Peter Sommerlad, committee member and C++Now presenter who often proposes
> additions to fill in holes in the standard library, asked me:
>
> Are you aware of anybody who tried to provide a boost::operators style of
> automatically providing additional operators with a single base class that
> through SFINAE injects all possible operators based on the ones defined in
> the template parameter? This won't give you the control of the current
> boost::operators, but would be much easier to teach.
>
> For example
>
> struct Me : make_operators_for<Me>{
> Me& operator+=(Me const&); // You get +
> bool operator<(Me const&) const; // You get all relops (<=> will make that
> obsolete)
> Me& operator++(); // you get postfix
> //etc.
> };
>
> Today we have the facilities and compilers to make that happen.
>
> What do you think? Who should I ask?
>
> Anyone doing any work on operators or have any thoughts about updating
> boost::operators?

At one point I made a CRTP base that conditionally provides operators,
but only for comparisons (==, !=, <, >, <=, >=). In order to not
negatively impact compilation and to preserve the operators as inline,
non-template friends, I had to make some different design decisions
from Boost.Operators.

First, in order to avoid subtleties, when you inherit from the CRTP
base you specify "dependencies" in addition to the ChildType. These
dependencies, in practice, are a list of all of the other bases and
members of the ChildType that might contribute to equivalence and
ordering. The operators are then conditionally provided by the CRTP
base if and only if any required operators are callable for each
dependency (i.e. == and != are only generated if the following is
valid for each dependency (std::declval<Dependency const&>() ==
std::declval<Dependency const&>()). One other difference from
Boost.Operators is that when inheriting from the base, you do *not*
implement operator== and operator<. Instead, the customization point
methodology was more similar to the boost::iterator_core_access
technique -- the ChildType implements "equals" and "less_than" (names
are actually obfuscated with "namespace" prefixes for the
customization point). The point of this is that the base type is the
one that conditionally generates == and <, in addition to the other
operators, based on the result from the dependency checks, meaning
that the ChildType is free to implement the customization points
totally unconstrained. Because of this, the ChildType doesn't have to
do any metaprogramming or manual SFINAE and the operators that are
generated are all inline, non-template friends.

I also want to echo Vicente's concerns about trying to generate all
operators. Ultimately, I do believe that they are better off
explicitly requested as-in Boost.Operators. In addition to that,
properly checking if the ChildType supports the necessary dependent
operators is not free, and it's also easy to inadvertently trigger the
check to be instantiated while the the ChildType is still incomplete
if the check takes place in a template that must be considered during
overload resolution by unrelated calls. I suspect that these
approaches will have too many subtleties to be useful in practice,
though it'd be nice to be proven wrong.

-- Matt Calabrese


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk