Using ranges allowed me to reduce boilerplate, so it's great but I could not find a way sort either in ascending order or descending order. The following snippet compiles just fine (g++ 10.2.0) and projection really simplifies the code, no need for a lambda.
struct Player {
double score_;
string name_;
};
vector<Player> players{
{10.0, "Thorin"}, {20.0, "Bombur"}, {15.0, "Bofur"}, {5.0, "Bifur"},
{ 2.0, "Balin"}, {25.0, "Kili" }, {23.0, "Fili"}, {4.0, "Dwalin"}
};
std::ranges::sort(players, std::ranges::less{}, &Player::score_ );
for(auto const &player : players) {
cout << "Name = " << std::left << setw(10) << player.name_
<< " Score = " << player.score_ << endl;
}
Now I need to have a boolean controlling ascending or descending order sort.
I would like to write a simple statement like this one:
std::ranges::sort(players, sort_ascending ? std::ranges::less() : std::ranges::greater() , &Player::score_);
but std::ranges::less
and std::ranges::greater
do not have the same type, so a ternary operator won't work.
error: operands to ‘?:’ have different types ‘std::ranges::less’ and ‘std::ranges::greater’
I could have a lambda with capture has shown below, but that's adding more lines of code. Any simple solution in sight?
auto mycompare = [sort_ascending](
const Player &a,
const Player &b) -> bool {
return sort_ascending ^ (b.score_ < a.score_);
};
std::ranges::sort(players, mycompare);
If
sort_ascending
is a run-time boolean value, then I don't think it's possible to choose a different function object inside the call tosort
, since the type of this object must be known at compile time.One option is to refactor this, with a couple of extra lines:
and call it like this:
Also, note that your last snippet is broken if
sort_ascending
is false. The predicate would end up being the negation ofstd::less
, which isstd::greater_equal
, notstd::greater
. This violates the strict-weak-ordering required bysort
, and you end up with undefined behavior.