paper: Defaulted comparison operators

261 views
Skip to first unread message

oleg.s...@gmail.com

unread,
Dec 26, 2013, 9:35:39 PM12/26/13
to std-pr...@isocpp.org
Hi all, I've written up the discussion into a paper, attempted to provide the minimal standardese and implemented a prototype in Clang. The draft is attached and I had asked Alisdair Meredith for a proposal number.

I realize that operator!=(), operator>=(), operator>() and operator<=() are contentious as people may choose to derive them in weird ways... So, I'll treat them as secondary goals. The primary goal is to generate the following member functions:

            bool operator==(const Thing &) const = default;      // 1a
            bool operator<(const Thing &) const = default;       // 1b

Oleg.
equality.html

David Rodríguez Ibeas

unread,
Dec 30, 2013, 11:40:44 AM12/30/13
to std-pr...@isocpp.org
I would try to think if there is a 'natural' way to transform that proposal so that the operators are not necessarily defined as member functions. While member functions map more naturally to the current uses of ' = default', free function operators are more natural to use, allowing the same conversions on both arguments. With the current wording, this code would fail to compile:

struct T {
   int value;
   T(int v) : value(v) {}             // implicit!
   bool operator==(T const &) = default;
};
int main() {
   return 1 == T(1);
}

While if we swap the order of the equality check it would compile. I find that asymmetry slightly disturbing. Maybe we could consider extending ' = default' to anything that is defined inside the class definition and allow:

struct T {
   // ...
   friend bool operator==(T const&, T const&) = default;
};

Additionally, I would like to have the wording explicitly mention that the operators can only be defaulted when they apply to exactly the same type that is being defined.

I have the feeling that I really want something more like:

struct T {
   operator== = default;
};

Or some other mechanism by which the type is not present in the signature and thus removes the potential for attempts to default cross-type comparisons... But this would require mayor changes in the grammar and is better avoided.

David


--
 
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

David Rodríguez Ibeas

unread,
Dec 30, 2013, 11:53:13 AM12/30/13
to std-pr...@isocpp.org
Another thought,

    Has there been any advances in SG7? Has there been any document produced?

Once/if we get at least compile time reflection for member variables this could be implemented at the library level. The standard library could provide templates inside 'rel_ops' that did the work. The following open question would be how to pull those operators so that lookup would find them.

One solution would be adding a tag type to the namespace and let users opt into those operators by means of inheritance from that tag (which would add the 'rel_ops' to ADL), which brings up the question of why that has not been done when ::std::rel_ops operators where added?

David

Remo Remotion

unread,
Dec 30, 2013, 1:21:59 PM12/30/13
to std-pr...@isocpp.org, oleg.s...@gmail.com

Hi.

There is another way to solve this problem, using compile-time reflection.

Here is how it could looks like by converting any class to the std::tuple.
    bool operator < (const S& l, const S& r) {
        return meta::as_ref_tuple(l) < meta::as_ref_tuple(r);
    }

This code is already working and can be compiled using experimental Clang implementation available here:
https://bitbucket.org/remotion/c-reflection

By using bases_trait it should be possible to add support for base classes too.
Of course it is also possible to use static_assert for custom compiler time diagnostics.

Remo

Ville Voutilainen

unread,
Dec 30, 2013, 1:28:10 PM12/30/13
to std-pr...@isocpp.org
On 30 December 2013 20:21, Remo Remotion <remot...@googlemail.com> wrote:
>
> Hi.
>
> There is another way to solve this problem, using compile-time reflection.
>
> Here is how it could looks like by converting any class to the std::tuple.
>
> bool operator < (const S& l, const S& r) {
> return meta::as_ref_tuple(l) < meta::as_ref_tuple(r);
> }
>
> This code is already working and can be compiled using experimental Clang
> implementation available here:
> https://bitbucket.org/remotion/c-reflection
>
> By using bases_trait it should be possible to add support for base classes
> too.
> Of course it is also possible to use static_assert for custom compiler time
> diagnostics.

Looks seriously interesting. Do you have any plans to tell SG7 (aka
the Reflection
Study Group of the standards committee) about this
work?

https://groups.google.com/a/isocpp.org/forum/?fromgroups#!forum/reflection

Philipp Maximilian Stephani

unread,
Dec 30, 2013, 5:11:48 PM12/30/13
to std-pr...@isocpp.org


On Mon Dec 30 2013 at 17:40:45, David Rodríguez Ibeas <dib...@ieee.org> wrote:
I have the feeling that I really want something more like:

struct T {
   operator== = default;
};

Or some other mechanism by which the type is not present in the signature and thus removes the potential for attempts to default cross-type comparisons... But this would require mayor changes in the grammar and is better avoided.


Something that looks somewhat natural and probably requires only localized grammar modifications could be:

using default operator==; 

Billy O'Neal

unread,
Dec 30, 2013, 5:31:19 PM12/30/13
to std-proposals
But the = default syntax matches the existing =default syntax for constructors and company.

Billy O'Neal
Malware Response Instructor - BleepingComputer.com


--

David Rodríguez Ibeas

unread,
Dec 31, 2013, 9:46:10 AM12/31/13
to std-pr...@isocpp.org
Maybe:

using operator== = default; 

A bit awkward to read the sequence of equal signs, but that would match a bit closer the existing using declaration syntax (although the '= default' means that it is no longer a declaration but also a definition...) Ignoring the exact details (bikesheding?), the more important point I was trying to get through is that I believe that operators should be implemented as free functions rather than member functions.

David

Billy O'Neal

unread,
Dec 31, 2013, 1:20:01 PM12/31/13
to std-proposals
Why? I fully understand that argument when you're comparing something to something else (e.g. std::string to char const* or similar), but there's no practical difference between a member and nonmember in this case (all the objects in question are of the class type).

Billy O'Neal
Malware Response Instructor - BleepingComputer.com


David Rodríguez Ibeas

unread,
Dec 31, 2013, 1:55:34 PM12/31/13
to std-pr...@isocpp.org
[Response inline]

On Tue, Dec 31, 2013 at 1:20 PM, Billy O'Neal <billy...@gmail.com> wrote:
Why? I fully understand that argument when you're comparing something to something else (e.g. std::string to char const* or similar), but there's no practical difference between a member and nonmember in this case (all the objects in question are of the class type).


I already provided an example in a previous message in this thread. Basically if the type allows for implicit conversions, then the free function implementation is symmetric with respect to types (as it should also be with respect to values), while the member function implementation will only allow the implicit conversion on the right hand side.


Quoting the previous example:

struct T {
   int value;
   T(int v) : value(v) {}             // implicit!
   bool operator==(T const &) = default;
};
int main() {
   return 1 == T(1);
}

If 'operator==' is implemented as a member function that code will fail, but 'T(1) == 1' will compile creating an asymmetry. If the operator is implemented as a free function both will compile, providing symmetry with respect to types in the expression.

David

inkwizyt...@gmail.com

unread,
Dec 31, 2013, 3:13:32 PM12/31/13
to std-pr...@isocpp.org, dib...@ieee.org
You can use syntax like this:
struct T {
   
int value;

   T
(int v) : value(v) {}

   
friend bool operator==(T const &, T const &) = default; //free function
};

Remotion

unread,
Jan 1, 2014, 11:00:11 AM1/1/14
to std-pr...@isocpp.org

Yes once it is a bit more mature.
This project is very new so I will need resolve most serious problems fist.
Any help is welcome.

Remo

oleg.s...@gmail.com

unread,
Jan 6, 2014, 12:20:06 AM1/6/14
to std-pr...@isocpp.org
Here is the updated paper with (I hope) a fully developed "Technical specifications" section.

P.S. I can publish a Clang patch that implements this proposal, if there is any interest.

Kind regards,
Oleg.
equality.html

oleg.s...@gmail.com

unread,
Jan 15, 2014, 12:53:01 PM1/15/14
to std-pr...@isocpp.org, oleg.s...@gmail.com
Here is another revision made after receiving a detailed feedback from Daniel Krügler. The biggest change is to do with "special function" treatment (which is a good thing to remove) yet the standard's references and wording have regressed in quality (not a good thing).

I would appreciate any help with wording, if anyone is interested in word-smithing and cross-references.

Thanks in advance!
Oleg.
equality.html
Reply all
Reply to author
Forward
0 new messages