Walletfox.com

How to sort a vector of user-defined objects with C++20 Ranges

In this article, we are going to sort a vector of user-defined objects, specifically Skyscrapers, using the syntax introduced by C++20 Ranges.

skyline

The compiler and the include header

If you choose to use the sort algorithm from C++20 Ranges, use #include <algorithm>. You can test your code in the Wandbox online compiler with the latest GCC.

#include <algorithm>
// ...

int main(){
    std::ranges::sort(...)
}

If you wish to use the sort algorithm from the Range-v3 library which currently offers much more functional programming machinery, use:

#include <range/v3/all.hpp>
// ...

int main(){
    ranges::sort(...)
}

How to use the sort algorithm from C++20 Ranges

The basic syntax goes as follows:

std::ranges::sort(Range, Comparator, Projection);

Range represents the range of objects to sort, such as std::vector<Skyscraper> skyscrapers.

Comparator, such as the function object std::ranges::less{} for ascending order, tells us how to compare one object to another.

Projection is the attribute according to which we would like to sort, e.g. &Skyscraper::height. The syntax is &Struct::attribute or &Class::accessor_method.

Sorting Skyscrapers

To demonstrate sorting we are going to use the class Skyscraper with the name and height attributes.

class Skyscraper
{
public:
    Skyscraper(std::string const& s, double h) : m_name(s), m_height(h){}
    auto name() const { return m_name;}
    auto height() const { return m_height;}
    void print() const {
            std::cout << m_name << " " << m_height << '\n'; 
    }
private:
    std::string m_name;
    double m_height;
};

We initialize the vector of four Skyscraper objects and pass it as an argument to the sorting algorithm. We use the default {}, i.e. ascending sorting order, as the second argument. The projection, i.e. the third argument height, denotes the attribute according to which to sort our vector.

int main(){
    auto skyscrapers = std::vector<Skyscraper> {
                        Skyscraper ("Empire State", 381),
                        Skyscraper ("Petronas", 452),
                        Skyscraper ("Burj Khalifa", 828),
                        Skyscraper ("Taipei", 509)
    };
    std::ranges::sort(skyscrapers, {}, &Skyscraper::height);
    
    std::ranges::for_each(skyscrapers, &Skyscraper::print);
}

The program results in the following output:

Empire State 381
Petronas 452
Taipei 509
Burj Khalifa 828

All the following snippets will sort the skyscrapers in ascending order:

std::ranges::sort(skyscrapers, {}, &Skyscraper::height);
std::ranges::sort(skyscrapers, std::ranges::less{}, &Skyscraper::height);
std::ranges::sort(skyscrapers, [](Skyscraper const& s1, Skyscraper const& s2){
                                    return s1.height() < s2.height();});

Sorting in descending order

To sort objects in descending order, we need to pass std::ranges::greater{}, instead of {} or std::ranges::less{}, as the the second argument.

std::ranges::sort(skyscrapers, std::ranges::greater{}, &Skyscraper::height);

This will produce the following output:

Burj Khalifa 828
Taipei 509
Petronas 452
Empire State 381

Sorting according to the name attribute

To sort objects in the default ascending order according to the name attribute, all we need to do is to pass the name in place of the third argument. As we already mentioned, this is called a projection.

std::ranges::sort(skyscrapers, {}, &Skyscraper::name);

This will produce the following output:

Burj Khalifa 828
Empire State 381
Petronas 452
Taipei 509

That's it. Run the code for C++20 Ranges or Range-v3.

Tagged: C++