All done with gcc 11.2 and libstdc++-11
Below code shows a variety of ways of using std::sort
and then std::ranges::sort
to sort builtins, user defined types and a std library type.
Only the std library type gives me trouble:
operator<
or operator<=>
because it is UB to add that to namespace std
. I think there is no way around this? (without using a lambda or similar)proj
parameter on the std lib type (but it works on the user defined type). Why? I was unable to penetrate the "wall of errors" enough to find out. gcc basically says: no known conversion for argument 3 from ‘<unresolved overloaded function type>’ to ‘std::identity’
..Update: My only theory on 2. above is that "unresolved overloaded function type" means that std::complex<double>::real
has 2 overloads. One which takes a parameter (the "setter") and one which doesn't (the "getter"). Is there a syntax which allows me to specify that I want the "address of" the one without a parameter?
Update2: Thanks to 康桓瑋 for pointing out in the comments that taking the address of a member function in std
is just UB anyway. However if I add a sum(int c)
overload to thing
(now added below) I get the same "unresolved overloaded" error. So the question remains, how can I select the one with no params. Or is there no way?
#include <algorithm>
#include <compare>
#include <complex>
#include <iostream>
#include <ranges>
#include <vector>
namespace std {
// this is UNDEFINED BEHAVIOUR!!! --- but "it works", so we know this option is what would be
// required, but is not available to us
std::partial_ordering operator<=>(const std::complex<double>& a, const std::complex<double>& b) {
return std::abs(a) <=> std::abs(b);
}
} // namespace std
// a user defined type
struct thing {
int x{};
int y{};
[[nodiscard]] int sum() const { return x + y; }
[[nodiscard]] int sum(int c) const { return x + y + c; } // added for update 2
friend std::strong_ordering operator<=>(const thing& a, const thing& b) {
return a.x + a.y <=> b.x + b.y;
}
friend bool operator==(const thing& a, const thing& b) { return a.x + a.y == b.x + b.y; }
friend std::ostream& operator<<(std::ostream& os, const thing& rhs) {
return os << "[" << rhs.x << "," << rhs.y << "]";
}
};
int main() {
// builtin types
auto ints = std::vector<int>{9, 10, 7, 8, 5, 6, 3, 4, 1, 2};
std::ranges::sort(ints);
std::ranges::sort(ints, {}, [](const auto& c) { return -c; });
for (const auto& e: ints) std::cout << e << " ";
std::cout << "\n";
auto things = std::vector<thing>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(things.begin(), things.end());
std::ranges::sort(things);
std::ranges::sort(things, {}, [](const auto& e) { return e.sum(); });
std::ranges::sort(things, [](const auto& a, const auto& b) { return a < b; }, {});
std::ranges::sort(things, {}, &thing::x);
std::ranges::sort(things, {}, &thing::sum); // COMPILE ERROR afte r update 2
for (const auto& e: things) std::cout << e << " ";
std::cout << "\n";
auto complexes = std::vector<std::complex<double>>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(complexes.begin(), complexes.end()); // requires operator< or <=> which is UB
std::ranges::sort(complexes); // requires operator<=> which is UB
std::ranges::sort(complexes, {}, [](const auto& c) { return std::abs(c); });
std::ranges::sort(complexes, {}, &std::complex<double>::real); // COMPILE ERROR!!
for (const auto& e: complexes) std::cout << e << " ";
std::cout << "\n";
return EXIT_SUCCESS;
}
You need to specify a comparison for T
if std::less<T>
is unavailable.
Both std::sort
and std::ranges::sort
require a strict weak ordering predicate, not operator<=>
(but that can supply one, via <
and std::less
)
template<typename T>
bool complex_less (std::complex<T> lhs, std::complex<T> rhs) { return abs(lhs) < abs(rhs); };
int main() {
auto things = std::vector<thing>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(things.begin(), things.end());
std::ranges::sort(things);
std::ranges::sort(things, {}, [](const auto& e) { return e.sum(); });
std::ranges::sort(things, [](const auto& a, const auto& b) { return a < b; }, {});
std::ranges::sort(things, {}, &thing::x);
std::ranges::sort(things, {}, static_cast<int (thing::*)()>(&thing::sum)); // need cast to disambiguate pointer-to-member
for (const auto& e: things) std::cout << e << " ";
std::cout << "\n";
auto complexes = std::vector<std::complex<double>>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(complexes.begin(), complexes.end(), complex_less<double>); // fine
std::ranges::sort(complexes, complex_less<double>); // also fine
std::ranges::sort(complexes, {}, [](const auto& c) { return std::abs(c); }); // still fine
std::ranges::sort(complexes, {}, [](const auto& c) { return c.real(); }); // fine
for (const auto& e: complexes) std::cout << e << " ";
std::cout << "\n";
return EXIT_SUCCESS;
}