Anybody interested in reviewing draft/presenting "anti" spaceship paper?

1,250 views
Skip to first unread message

libb...@gmail.com

unread,
Feb 16, 2019, 4:43:01 PM2/16/19
to ISO C++ Standard - Future Proposals
Hi everybody,

I do not participate in C++ standardization, but I dislike <=> so much that I am considering writing an proposal to stop it.
I know it is futile to try to revert feature this big this late in C++20 cycle, it is mostly about doing the right thing even when you have zero chance of success. :)

Draft  is here.

Feel free to comment objections, requests for clarification... in the document or reply here.
Also since I do not attend C++ standardization meetings if somebody likes the draft and is interested in presenting it that would also be great.

regards,
Ivan

Andrew Tomazos

unread,
Feb 16, 2019, 6:37:02 PM2/16/19
to std-pr...@isocpp.org
It isn't too late.  It's most likely too late for Kona which is starting tommorow, but you could submit a paper into the next mailing for Cologne.

If you are not willing to show up to Cologne and defend your paper, you will need to find a champion to present it.  I would start by refining the paper, submit it into a mailing, and then look for a champion once the mailing has been posted.

I can help find you one if you can't find one yourself.



--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/544de10d-be39-4c83-a211-b888a34724c3%40isocpp.org.

Nicol Bolas

unread,
Feb 16, 2019, 10:04:08 PM2/16/19
to ISO C++ Standard - Future Proposals
Your strongest argument was about == and != being potentially faster, and that has already been changed (ie: EWG approved. I don't believe CWG has approved wording), pursuant to P1190 and P1185.

I find most of the rest of your arguments to be specious and highly opinionated with little factual basis behind them. I was particularly amused by your argument that the "average C++ developer" cannot be expected to be able to learn about anything within the standard library's namespace. I'm pretty sure C++ programmers are smart enough at a minimum to learn by route (ie: include file X and the words Y can be used to mean Z).

Also, performance is not really the reason for `<=>`; it's convenience, consistency, and ease of use without sacrificing performance. So the fact that nobody can prove that `<=>` is undeniably faster in some case not really relevant. So long as it isn't slower, it's a net-gain.

Oh, and as for real code that is more readable with `<=>` than without it, just look at any implementation of `std::optional`.

Lastly, a typo: you cite a section of P0893, but the section you meant to cite is 5.1.3, not 5.3.1.

Ivan Matek

unread,
Feb 17, 2019, 1:43:33 PM2/17/19
to std-pr...@isocpp.org
On Sun, Feb 17, 2019 at 4:04 AM Nicol Bolas <jmck...@gmail.com> wrote:
Your strongest argument was about == and != being potentially faster, and that has already been changed (ie: EWG approved. I don't believe CWG has approved wording), pursuant to P1190 and P1185.

Not really. The fact that proposal was broken for == and != shows  how "powerful" and "elegant" it was, but that is not the strongest argument(IMO, others may disagree).
Strongest argument is not to butcher the core language, for feature that has no large benefits.
I find most of the rest of your arguments to be specious and highly opinionated with little factual basis behind them.
Unlike scientific fact that we need <=>? Funny how during my career I have never heard anybody in the real world complain(and I like gossiping about C++ with coworkers) about missing a 3 way comparison(people complain about verbosity of <, == ... but like I tried to explain that does not mean we need <=>).
I was particularly amused by your argument that the "average C++ developer" cannot be expected to be able to learn about anything within the standard library's namespace.
 strawman(that is not what I said).
 
Also, performance is not really the reason for `<=>`; it's convenience, consistency, and ease of use without sacrificing performance. So the fact that nobody can prove that `<=>` is undeniably faster in some case not really relevant. So long as it isn't slower, it's a net-gain.
I disagree that <=> is convenient or easy to use.
So assuming that for me <=> should prove it's performance is worth the ugliness it brings to core language.

Oh, and as for real code that is more readable with `<=>` than without it, just look at any implementation of `std::optional`.
Standardization should not care about optional like library code. C++ should be optimized for millions of regular developers, not 20-100 people that write boost/std libraries.

Tnx for replying, and informing me about a typo.

Arthur O'Dwyer

unread,
Feb 17, 2019, 5:55:56 PM2/17/19
to ISO C++ Standard - Future Proposals
On Saturday, February 16, 2019 at 10:04:08 PM UTC-5, Nicol Bolas wrote:
[...]
Oh, and as for real code that is more readable with `<=>` than without it, just look at any implementation of `std::optional`.

Barry Revzin's D1189 shows this code for optional::operator<=>. Of course D1189 does not propose to remove any of the six existing relational operators from `optional` because that would be a breaking change, but we can ignore that for our purposes. Let's just say that we're writing a hypothetical `optional`-alike library, and this would be the only operator we needed.

template <typename T>
class optional {
    
    template <ThreeWayComparableWith<T> U>
    compare_3way_type_t<T,U> operator<=>(optional<U> const& rhs) const;
    {
        if (has_value() && rhs.has_value()) {
            return **this <=> *rhs;
        } else {
            return has_value() <=> rhs.has_value();
        }
    }
    
};

Whereas, a real live example of `optional::operator>=` from libc++ looks like this:

template <class _Tp, class _Up>
_LIBCPP_INLINE_VISIBILITY constexpr
enable_if_t<
    is_convertible_v<decltype(_VSTD::declval<const _Tp&>() >=
        _VSTD::declval<const _Up&>()), bool>,
    bool
>
operator>=(const optional<_Tp>& __x, const optional<_Up>& __y)
{
    if (!static_cast<bool>(__y))
        return true;
    if (!static_cast<bool>(__x))
        return false;
    return *__x >= *__y;
}

So we're talking about the difference between

    auto operator<=>(const optional& o) const { return (has_value() && o.has_value()) ? (**this <=> *o) : (has_value() <=> o.has_value()); }

and

    auto operator<(const optional& o) const { return has_value() ? (o.has_value() ? (**this < *o) : false) : o.has_value(); }
    auto operator<=(const optional& o) const { return !(o < *this); }
    auto operator>(const optional& o) const { return (o < *this); }
    auto operator>=(const optional& o) const { return !(*this < o); }
    auto operator==(const optional& o) const { return has_value() ? (o.has_value() && **this == *o) : !o.has_value(); }
    auto operator!=(const optional& o) const { return !(**this == *o); }

The former one-liner assumes that `T` supports operator<=>. The latter six-liner assumes that `T`'s relational operators all have the mathematical relationships we naturally assume they should, such as that (x<y) iff (y>x). The actual STL version of std::optional doesn't assume either of these things, which is why D1189 can't propose to replace the six-liner with the one-liner, and why libc++'s code has to be so fiddly.

See
for more details on why right now users will never be able to use the <=> operator in their actual code.
I think there is a proposal in flight to fix this by making `a <=> b` magically rewrite itself into `(a < b) ? less : (a > b) : greater : equal`, or something like that, if there is no viable operator<=> in scope; but I couldn't find that proposal in a quick search.

–Arthur

Gaetano Checinski

unread,
Feb 18, 2019, 10:02:32 AM2/18/19
to std-pr...@isocpp.org, libb...@gmail.com
Personally I don't think we really need `<=>` as it can easily simulated with some TypeClass based Meta Programming.
In fact, I believe it's a technique that is underused in the C++ community.

Here an example of this approach: https://godbolt.org/z/2IIXlh

Maybe we should standardize a set of Type-Classes and Customization Points instead?

I'm happy to elaborate.

Mailtrack Sender notified by
Mailtrack 02/18/19, 2:58:57 PM

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/544de10d-be39-4c83-a211-b888a34724c3%40isocpp.org.


--
Regards,

Gaetano Checinski
Founder of Loopperfect

Gašper Ažman

unread,
Feb 18, 2019, 11:08:31 AM2/18/19
to std-pr...@isocpp.org, libb...@gmail.com
I disagree. Synthesizing <=> from the class members will only be possible after reflection *and* generation are in the language, and rewriting x < y into (x <=> ) < 0 will never be possible without a function call. The language does that really quickly and automatically now.

<=> is *way* more ergonomic for rule-of-zero stuff than any metaprogramming can do ATM. <=> does a really important thing. I don't agree we should take it out. Barry's proposals fix it to correctness. And yes, I've read (and discussed with Barry and Jeff) all of them.

Nicol Bolas

unread,
Feb 18, 2019, 11:09:25 AM2/18/19
to ISO C++ Standard - Future Proposals, libb...@gmail.com, gaetano....@gmail.com
On Monday, February 18, 2019 at 10:02:32 AM UTC-5, Gaetano Checinski wrote:
Personally I don't think we really need `<=>` as it can easily simulated with some TypeClass based Meta Programming.
In fact, I believe it's a technique that is underused in the C++ community.

Here an example of this approach: https://godbolt.org/z/2IIXlh

Maybe we should standardize a set of Type-Classes and Customization Points instead?

That doesn't actually solve everything that `<=>` does. It doesn't solve generating comparison operations (that would require reflection, which is still a long way off). It certainly doesn't solve the literal types as non-type template parameter thing.

I'm also not sure how this would interact in a modular world. Ideally, all I should have to export from your example is the `Custom` class and `CustomTrait`. But since the latter is actually a specialization of a template that exists in a completely different namespace and module, I also have to export the trait from the standard library module. And I would probably have to export the `operator` overloads based on that trait. Or just have my module export the entire standard library module containing `CustomTrait`... which is kind of the opposite of how modules are supposed to work.

Overall, I don't know why we should prefer a half-measure library solution like this to a proper language solution. Especially when the language solution has already been approved by CWG. You're basically advocating removing something from the WP in order to replace it with a weaker version of itself.

Vishal Oza

unread,
Feb 18, 2019, 6:11:38 PM2/18/19
to ISO C++ Standard - Future Proposals
I think that the spaceship operator <=> feature needs more information on how it should be used for common programmer. I do not think that the spaceship operator <=> should be used removed from the standard. I would like some more books and articles on how to use this feature both as a run-time feature and a compile-time feature.

Gašper Ažman

unread,
Feb 18, 2019, 6:22:12 PM2/18/19
to std-pr...@isocpp.org
It's on my to-do list - but as a rough tldr: use <=> to implement all the ordering operators, but use < in calling it.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Vishal Oza

unread,
Feb 18, 2019, 6:34:50 PM2/18/19
to ISO C++ Standard - Future Proposals
Thank you I have a simple uses case but this is more like a hello world and not a production ready example I would like more examples to refine my understanding

#include <iostream>
#include <compare>
int main()
{
   
auto a = 75;
   
auto b = 175;
   
if (auto comp = a <=> b; comp > 0)
       
std::cout << "greater\n";
   
else if (comp == 0)
        std
::cout << "equal\n";
   
else
        std
::cout << "lesser\n";
}

Dejan Milosavljevic

unread,
Feb 25, 2019, 6:18:24 AM2/25/19
to std-pr...@isocpp.org
Agree to have three-way operator.
Disagree that this operator should be consisted of <=>.
Tilde might good candidate or double tilde. Hmm scratch that heavy collide with bit not.

Probably I miss something.
What is literal name of this operator?

From my experience  'hello world' should be 

switch(spaceship b)
{
   case(1):
std::cout << "greater\n"; break;
   case(0): std::cout << "equal\n"; break;
   case(-1): std::cout << "lesser\n"; break;
}


Barry Revzin

unread,
Feb 25, 2019, 8:40:55 AM2/25/19
to ISO C++ Standard - Future Proposals
On Saturday, February 16, 2019 at 3:43:01 PM UTC-6, libb...@gmail.com wrote:
<=> will be in C++20, and P1185 was just adopted in Kona, so it's not really worth going through some of the arguments you make. But rather, I thought it'd be worth trying to point something else out. The syntax you're suggesting is:

class C{

 public:

   default operator: <,<=, ==, !=,>=,>;

 private:

   std::string a,b;

   int c;

};


I do agree with the argument you make that most of the things you need to write can be assumed. But you never actually try to define what this means. Now, "what this means" for operators like ==, !=, <, and > seems pretty straightforward so you might be wondering why I am prodding here. But what does <= do? You have two choices:

// option 1
bool operator<=(C const& lhs, C const& rhs) {
   
return lhs < rhs || lhs == rhs;
}

// option 2
bool operator<=(C const& lhs, C const& rhs) {
   
return !(rhs < lhs);
}

Which do you pick? The "obvious" answer might be option 2 - it does fewer operations, of course! But the way we get to option 2 is that we assume that if a <= b is true precisely when a > b is false. That assumption only holds when we have trichotomy - when we have a total order. If we have a partial order, the transformation is invalid and will give you incorrect answers. Consider float: 1.0f <= NaN needs to be false - but with option 2 we'd get true. 

In other words, your choice is:
  • Default <= to use option 1. This is a large pessimization for totally odrered types.
  • Default <= to use option 2. This will give incorrect answers for partially ordered types.
Which do you pick? And how does the user override the choice if they need the other? What if they don't know which they need?

This is a big advantage of <=>: you can easily specify the total vs partial difference, and then trivially propagate that through. 
Message has been deleted

Ivan Matek

unread,
Mar 1, 2019, 11:47:41 AM3/1/19
to std-pr...@isocpp.org
On Mon, Feb 25, 2019 at 2:40 PM Barry Revzin <barry....@gmail.com> wrote:
On Saturday, February 16, 2019 at 3:43:01 PM UTC-6, libb...@gmail.com wrote:
<=> will be in C++20
Disappointed, not surprised. :)
 

But what does <= do? You have two choices:
Which do you pick?
Why do you not just use member's <=?

If you do not want to bother answering since this is futile discussion that is also fine. :)

Also one more thing I realized rather recently(it is not in the document) but may be relevant for C++26 so I wonder if you could give opinion.
When Herb does his thing with reflection (C++26?) spaceship will be kind of suboptimal because people will want to reflect on members and
be able to specify that first you compared cheap members(this can be guessed by type) or based on some knowledge of usage, for example int room_number_ has high entropy in use so use that member first.

So in C++26 we may get this:

bla operator<=>(const C& other) = cmp_cheap_first;

where cmp_cheap_first is some heuristic reflection that knows that comparing built in types is cheap, then it assumes that sizeof POD is proportional to their cost, and then it goes over rest of members.


Another thing we may want is enable users to specify good or  bad comparisons while compiler does the rest.
So consider the
bla operator<=>(const Guest& other) = memberwise(room_number_,...); // room number unlikely to be same compare that first
or
bla operator<=>(const Guest& other) = memberwise(..., title_); // most titles are "Mr" or "Mrs", do whatever you want but compare title_ last

regards,
Ivan





Barry Revzin

unread,
Mar 1, 2019, 2:31:10 PM3/1/19
to ISO C++ Standard - Future Proposals
On Friday, March 1, 2019 at 10:47:41 AM UTC-6, Ivan Matek wrote:


On Mon, Feb 25, 2019 at 2:40 PM Barry Revzin <barry....@gmail.com> wrote:
On Saturday, February 16, 2019 at 3:43:01 PM UTC-6, libb...@gmail.com wrote:
<=> will be in C++20
Disappointed, not surprised. :)
 

But what does <= do? You have two choices:
Which do you pick?
Why do you not just use member's <=?

You can do. It just means that if you're a type that provides something other than lexicographic, member-wise comparison, you have to write both < and <= (in addition to ==). Whichever way you go about doing (which typically will just be <= just invoking <), that still seems worse than just being able to write one <=>. 

The only real benefit of this approach vs the <=> we have in C++20 is slightly shorter defaulting of comparisons? That doesn't seem like a good tradeoff when comparing the two options.


If you do not want to bother answering since this is futile discussion that is also fine. :)

Also one more thing I realized rather recently(it is not in the document) but may be relevant for C++26 so I wonder if you could give opinion.
When Herb does his thing with reflection (C++26?) spaceship will be kind of suboptimal because people will want to reflect on members and
be able to specify that first you compared cheap members(this can be guessed by type) or based on some knowledge of usage, for example int room_number_ has high entropy in use so use that member first.


I don't see at all how this makes spaceship "suboptimal". The defaulted <=> won't do what you want, but that doesn't prevent you from writing a non-defaulted <=>. But writing these kinds of comparisons using <=> is still much easier than without <=>:

auto operator<=>(Type const& rhs) const {
   
constexpr vector<meta::info> members = get_sorted_members_for_comparison();
   
for ... (constexpr meta::info member : members) {
       
auto cmp = this->idexpr(member) <=> rhs.idexpr(member);
       
if (cmp != 0) {
            return cmp;
       
}
   
}
   
return strong_ordering::equal;
}

This needs some slight massaging to get the return type right, but otherwise it's quite nice.

Barry Revzin

unread,
Mar 1, 2019, 2:49:21 PM3/1/19
to ISO C++ Standard - Future Proposals


On Friday, March 1, 2019 at 1:31:10 PM UTC-6, Barry Revzin wrote:
On Friday, March 1, 2019 at 10:47:41 AM UTC-6, Ivan Matek wrote:


On Mon, Feb 25, 2019 at 2:40 PM Barry Revzin <barry....@gmail.com> wrote:
On Saturday, February 16, 2019 at 3:43:01 PM UTC-6, libb...@gmail.com wrote:
<=> will be in C++20
Disappointed, not surprised. :)
 

But what does <= do? You have two choices:
Which do you pick?
Why do you not just use member's <=?

You can do. It just means that if you're a type that provides something other than lexicographic, member-wise comparison, you have to write both < and <= (in addition to ==). Whichever way you go about doing (which typically will just be <= just invoking <), that still seems worse than just being able to write one <=>. 

The only real benefit of this approach vs the <=> we have in C++20 is slightly shorter defaulting of comparisons? That doesn't seem like a good tradeoff when comparing the two options.

And to elaborate, it really is only slightly shorter:

default operator: <, <=, ==, !=, >=, >;      // proposed here
auto operator<=>(C const&) const = default;  // C++20


Ivan Matek

unread,
Mar 1, 2019, 3:12:58 PM3/1/19
to std-pr...@isocpp.org
On Fri, Mar 1, 2019 at 8:31 PM Barry Revzin <barry....@gmail.com> wrote:
The only real benefit of this approach vs the <=> we have in C++20 is slightly shorter defaulting of comparisons? That doesn't seem like a good tradeoff when comparing the two options.

Also return types differ. And people make fun of me for this, but I really do feel this is confusing to average C++ developer, and that confusion is not worth the benefits.
 
I don't see at all how this makes spaceship "suboptimal". The defaulted <=> won't do what you want, but that doesn't prevent you from writing a non-defaulted <=>. But writing these kinds of comparisons using <=> is still much easier than without <=>:
It is suboptimal because you are writing logic instead of doing something in a declarative way.
I mean your code is nice because you have for each member so code is less fragile than if it was listing every member by name, but for something so common I would hope for language support for declarative way of doing that.
Or at least a way to write something declarative using reflection.
In other words I would like to have a way to write a metafunction(or how do you want to call it) that I can use like this
auto operator<=>(Type const& rhs) const = my_meta_fn(room_number_, ...);
In other words my_meta_fn creates the function based on arguments(room_number_,....) (and implicit T argument) and then that function is assigned to the operator <=>.
I know people can use CRTP and other wizardry for this, but this is what I would like to see since it is "locally readable"(like lambdas are more easier to read than some struct 50 lines above the usage).



Nicol Bolas

unread,
Mar 1, 2019, 5:19:50 PM3/1/19
to ISO C++ Standard - Future Proposals
On Friday, March 1, 2019 at 3:12:58 PM UTC-5, Ivan Matek wrote:
On Fri, Mar 1, 2019 at 8:31 PM Barry Revzin <barry....@gmail.com> wrote:
The only real benefit of this approach vs the <=> we have in C++20 is slightly shorter defaulting of comparisons? That doesn't seem like a good tradeoff when comparing the two options.

Also return types differ.

Differ from what?
 
And people make fun of me for this, but I really do feel this is confusing to average C++ developer, and that confusion is not worth the benefits.

But you haven't really explained why it is confusing. Or at least, the reasons you have given are unconvincing. Statements like "core language feature that will be often used and returns one of magic types that lives in std:: (and none of their names mean anything to average C++ developer) is not teachable." are just not really going to convince anyone that `std::strong_order operator<=>(const my_type&) = default;` is unteachable.

It literally says what it does:  "strongly-ordered comparison with my_type, generated by default"; you can read the code left-to-right, doing some basic substitutions, and you get conventional English. If someone can't teach that, then that's not a problem with the feature; it's either with the students or the teacher.

I don't see at all how this makes spaceship "suboptimal". The defaulted <=> won't do what you want, but that doesn't prevent you from writing a non-defaulted <=>. But writing these kinds of comparisons using <=> is still much easier than without <=>:
It is suboptimal because you are writing logic instead of doing something in a declarative way.
I mean your code is nice because you have for each member so code is less fragile than if it was listing every member by name, but for something so common I would hope for language support for declarative way of doing that.

The "common" thing is to do member-wise comparison. If you need a less common thing, then you have to spell it out explicitly. I fail to see what is "suboptimal" about this.


Or at least a way to write something declarative using reflection.
In other words I would like to have a way to write a metafunction(or how do you want to call it) that I can use like this
auto operator<=>(Type const& rhs) const = my_meta_fn(room_number_, ...);
In other words my_meta_fn creates the function based on arguments(room_number_,....) (and implicit T argument) and then that function is assigned to the operator <=>.

But you cannot have that without `operator<=>` being a real construct. Why should we wait for generative reflection when we can get something useful now? It's not like the current syntax makes generative reflection-based implementations impossible or something.

Plus, a defaulted `<=>` operator gives the compiler more opportunities to optimize things. Being explicit and direct about what you want (in order, member-wise comparison) allows the compiler to play games that requiring the user to always spell everything out in some metafunction wouldn't allow.

Reply all
Reply to author
Forward
Message has been deleted
0 new messages