Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
139 views
in Technique[技术] by (71.8m points)

c++ - What are the breaking changes caused by rewritten comparison operators?

There are some new rules about rewritten comparison operators in C++20, and I'm trying to understand how they work. I've run into the following program:

struct B {};

struct A
{
    bool operator==(B const&);  // #1
};

bool operator==(B const&, A const&);  // #2

int main()
{
  B{} == A{};  // C++17: calls #2
               // C++20: calls #1
}

which actually breaks existing code. I'm a little surprised by this; #2 actually still looks better to me :p

So how do these new rules change the meaning of existing code?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

That particular aspect is a simple form of rewriting, reversing the operands. The primary operators == and <=> can be reversed, the secondaries !=, <, >, <=, and >=, can be rewritten in terms of the primaries.

The reversing aspect can be illustrated with a relatively simple example.

If you don't have a specific B::operator==(A) to handle b == a, you can use the reverse to do it instead: A::operator==(B). This makes sense because equality is a bi-directional relationship: (a == b) => (b == a).

Rewriting for secondary operators, on the other hand, involves using different operators. Consider a > b. If you cannot locate a function to do that directly, such as A::operator>(B), the language will go looking for things like A::operator<=>(B) then simply calculating the result from that.

That's a simplistic view of the process but it's one that most of my students seem to understand. If you want more details, it's covered in the [over.match.oper] section of C++20, part of overload resolution (@ is a placeholder for the operator):

For the relational and equality operators, the rewritten candidates include all member, non-member, and built-in candidates for the operator <=> for which the rewritten expression (x <=> y) @ 0 is well-formed using that operator<=>.

For the relational, equality, and three-way comparison operators, the rewritten candidates also include a synthesized candidate, with the order of the two parameters reversed, for each member, non-member, and built-in candidate for the operator <=> for which the rewritten expression 0 @ (y <=> x) is well-formed using that operator<=>.


Hence gone are the days of having to provide a real operator== and operator<, then boiler-plating:

operator!=      as      !  operator==
operator>       as      ! (operator== || operator<)
operator<=      as         operator== || operator<
operator>=      as      !  operator<

Don't complain if I've gotten one or more of those wrong, that just illustrates my point on how much better C++20 is, since you now only have to provide a minimal set (most likely just operator<=> plus whatever else you want for efficiency) and let the compiler look after it :-)


The question as to why one is being selected over the other can be discerned with this code:

#include <iostream>

struct B {};
struct A {
    bool operator==(B const&) { std::cout << "1
"; return true; }
};
bool operator==(B const&, A const&) { std::cout << "2
"; return true; }

int main() {
  auto b = B{}; auto a = A{};

           b ==          a;  // outputs: 1
  (const B)b ==          a;  //          1
           b == (const A)a;  //          2
  (const B)b == (const A)a;  //          2
}

The output of that indicates that it's the const-ness of a deciding which is the better candidate.

As an aside, you may want to have a look at this article, which offers a more in-depth look.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...