The C++20 standard's three-way comparison model (<=>
) is versatile, but there are types and situations where it might not fit naturally. Here's how C++20 handles such cases and what you can do:
For complex types, where simple comparison logic might not be straightforward, you can still define a meaningful <=>
operator by focusing on the most relevant attributes for comparison.
Consider complex numbers where comparing the magnitude is more meaningful than comparing the real and imaginary parts separately:
#include <cmath>
#include <compare>
#include <complex>
#include <iostream>
class ComplexNumber {
public:
ComplexNumber(double real, double imag)
: Real{real}, Imag{imag} {}
std::partial_ordering operator<=>(
const ComplexNumber& Other) const {
double mag1 = std::sqrt(
Real * Real + Imag * Imag
);
double mag2 = std::sqrt(
Other.Real * Other.Real + Other.Imag * Other.Imag
);
return mag1 <=> mag2;
}
bool operator==(const ComplexNumber& Other) const {
return Real == Other.Real && Imag == Other.Imag;
}
private:
double Real, Imag;
};
int main() {
ComplexNumber num1{3, 4};
ComplexNumber num2{5, 12};
if (num1 < num2) {
std::cout
<< "num1 has a smaller magnitude than num2\n";
} else if (num1 > num2) {
std::cout
<< "num1 has a larger magnitude than num2\n";
} else {
std::cout
<< "num1 and num2 have the same magnitude\n";
}
}
num1 has a smaller magnitude than num2
For types where comparison doesn’t make sense (like a function object or a file handle), you should not define <=>
. Instead, you might provide specialized comparison functions if any form of comparison is needed.
If your type has a unique way of determining order, you can encapsulate this in the <=>
operator. For instance, a Version
class comparing major, minor, and patch numbers can be handled like this:
#include <compare>
#include <iostream>
class Version {
public:
Version(int major, int minor, int patch)
: Major{major}, Minor{minor}, Patch{patch} {}
std::strong_ordering operator<=>(
const Version& Other) const {
if (auto cmp = Major <=> Other.Major; cmp != 0)
return cmp;
if (auto cmp = Minor <=> Other.Minor; cmp != 0)
return cmp;
return Patch <=> Other.Patch;
}
bool operator==(const Version& Other) const {
return std::tie(Major, Minor, Patch) ==
std::tie(Other.Major, Other.Minor, Other.Patch);
}
private:
int Major, Minor, Patch;
};
int main() {
Version v1{1, 0, 0};
Version v2{1, 1, 0};
if (v1 < v2) {
std::cout << "v1 is older than v2";
} else if (v1 > v2) {
std::cout << "v1 is newer than v2";
} else {
std::cout << "v1 and v2 are the same version";
}
}
v1 is older than v2
The C++20 standard is flexible and allows you to implement <=>
even for complex or niche cases.
By focusing on meaningful comparisons and encapsulating the logic within the <=>
operator, you can leverage the power of three-way comparison for a wide range of types.
Answers to questions are automatically generated and may not have been reviewed.
A guide to simplifying our comparison operators using C++20 features