C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Standard attribute [[nocompare]] for defaulted comparisons

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Mon, 16 Jan 2023 23:14:11 -0500
On Mon, Jan 16, 2023 at 10:56 PM Arthur O'Dwyer via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> On Mon, Jan 16, 2023 at 4:26 PM joeri _ via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>
>> The introduction of a standard attribute [[nocompare]] which can be used to indicate that a suboject of T should be omitted from a defaulted comparison.
>>
>>
>>
>> struct s {
>>
>> int id;
>>
>> [[nocompare]] std::string name;
>>
>> auto operator<=>(s const&) const = default;
>>
>> };
>
>
> This wouldn't be a good use of an attribute — not because "attributes should be ignorable," but mainly because attributes are (like keywords) expensive to specify and implement and teach. It seems like there ought to be a clever way to get this behavior without any language extensions.
> The first thing that came to mind was to wrap `name_` in a helper type that would always compare equal; something like this:
> struct S {
> int id_;
> struct Helper {
> std::string name_;
> auto operator<=>(const Helper&) const { return std::strong_ordering::equal; }
> auto operator==(const Helper&) const { return true; }
> } h;
> auto operator<=>(const S&) const = default;
> };
>
> You can even get the inner `<=>` and `==` for free(-ish) by making `Helper` inherit from `std::monostate`, or any other type whose `operator<=>` does the right thing.
> // https://godbolt.org/z/v4nzKjbb5
> struct S {
> int id_;
> struct Helper : std::monostate {
> Helper(std::string n) : name_(std::move(n)) {}
> std::string name_;
> } h;
> S(int id, std::string name) : id_(id), h(std::move(name)) {}
> auto operator<=>(const S&) const = default;
> };
> I'm not saying this solution as-is should be considered better or cleaner than `[[nocompare]] std::string name_;` — but it suggests to me that there is some clever idiom waiting to be discovered here within the current language.

It seems to me that this would be a good use of a template type:

```
template<typename T>
struct noncompare
{
  T value_; //I prefer leaving it as an aggregate
  auto operator==(noncompare const&) const {return true;}
  auto operator<=>(noncompare const&) const {return
std::strong_ordering::equal;}

  operator T&() {return value_;}
  operator T const&() const {return value_;}
};
```

Obviously these interfere somewhat with actions on the contained `T`,
but it ought to work adequately.

That being said, the attribute syntax has an advantage: it can play
around with structural types. At present, you can overload
`operator==` for structural types, but they don't matter; equality for
template instantiation purposes only cares about binary equality. If
we allow attributes to turn off a member's equality, we could allow
such types to remain structural, just ignoring the members that aren't
compared.

Received on 2023-01-17 04:14:30