7
votes

Say I'm writing an int wrapper and need to provide every single operator overload. Must the author list out every single one, or can it auto-generate any based on what the author has provided? Can/does the compiler infer any new auto-defined operators from existing ones?

If I define operator==, does it give me an operator!= automatically? Or vice-versa?

If I define operator++(), do I get operator++(int) for free? Or vice versa?

How about the += type business? Can it combine existing definitions of operator+ with operator= to generate operator+=? In theory it should be possible but does it?

Same question for >= to <, etc, or do I have to fully list out definitions for >,>,>=,<=?

4
There's a proposal for comparison operator defaulting.user657267
It is easy enough to test this.juanchopanza
boost has some operators based on others: boost.org/doc/libs/1_59_0/libs/utility/operators.htmstefaanv
@user657267: Thanks, that proposal makes sense. I wish there was one for operator++() to operator++(int) as well, I feel like you should only have to give one of those.VoidStar

4 Answers

4
votes

In the core language the various operators are independent. Some are defined in terms of others, but if overload resolution for an operator invocation fails then there is no attempt to express that invocation in terms of other operators. When that's desired it can easily be expressed by the programmer (the opposite, turning off such machinery, would probably be more difficult).

There is a set of relational operator overloads in std::rel_ops that client code can use, defined in terms of < and ==.

You can easily write a mixin-class that provides relational operators in terms of < and ==, or in terms of a tri-valued compare function. That was the original motivation for the Curiously Recurring Template Pattern, called the Barton-Nackman trick.

3
votes

No.

C++ has no inference rules in the core language, so even defining say + it doesn't assume anything about +=... they're just (as far as the language goes) totally unrelated.

Consider that the << (left bit-shift operator) in the standard library has been overloaded to mean "output to stream"... just because of the look and of a sensible priority and associativity.

1
votes

C++ 20 operator <=>

It appears that with C++20, std::rel_ops is deprecated, and defaulting <=> will automatically give ==, !=, <, >, <=, >= for free.

Adapted from https://en.cppreference.com/w/cpp/language/default_comparisons:

main.cpp

#include <cassert>
#include <compare>
#include <set>

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
};

int main() {
    Point pt1{1, 1}, pt2{1, 2};

    // Just to show it Is enough for `std::set`.
    std::set<Point> s;
    s.insert(pt1);

    // Do some checks.
    assert(!(pt1 == pt2));
    assert( (pt1 != pt2));
    assert( (pt1 <  pt2));
    assert( (pt1 <= pt2));
    assert(!(pt1 >  pt2));
    assert(!(pt1 >= pt2));
}

Compile and run:

sudo apt install g++-10
g++-10 -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

More details at: What is the <=> operator in C++?

Tested on Ubuntu 20.04, GCC 10.2.0.

1
votes

C++20 adds a feature allowing the language to do something like this for relational (<, >, <=, >=) and equality operators (== and !=).

When using equality operators, the system can attempt to reverse the order of the operands (for equality testing of different types) as well as negate the result in order to find an appropriate operator== overload. That is, if you only implement operator== for equality testing A with B, this will also allow you to equality test B with A, and inequality test them as well.

Note that the compiler is not generating operator functions for you. It is instead modifying the actual place where it is invoking the operator. That is, it turns b != a into !(a == b) in order to find an appropriate == operator.

For <=>, it gets applied to all of the relational operators (but not the equality operators) in much the same way. The system will rewrite a < b to be (a <=> b) < 0 or (b <=> a) > 0 as needed to find a matching overloaded <=> operator.

Additionally, you can = default any of the comparison operators, which does a subobject-wise, in order comparison of the subobjects of the type in question (you can only default comparisons of the same type). If you default the == operator, then per the above rules, you effectively get != as well. If you default <=>, you get all of the relational operators via rewriting.

If you default <=> without defaulting ==, then the system will also generate a default ==, so defaulting <=> alone gives you all of the comparison operators.