Pages

std::sort

To use the STL sort algorithm on a container, the type used by the container should define a less-than operator.

But what if we are required to order the container in different ways? We could pass to sort a predicate that defines our specific less-than operator.

If we are not using boost or C++0x usually this means we create a specific functor just for that.

Here we see an example to accomplish this task using boost::bind or a lambda expression:

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <algorithm>
#include <iterator>

#include "boost/bind.hpp"

using namespace std;

namespace
{
class PersonalInfo
{
string name_;
string surname_;
unsigned int age_;

public:
PersonalInfo(const string& n, const string& s, unsigned int age) :
name_(n), surname_(s), age_(age) {}

string name() const { return name_; }

string surname() const { return surname_; }

unsigned int age() const { return age_; }

// 1. define the operator less-than based on the first name
friend bool operator< (const PersonalInfo& lhs, const PersonalInfo& rhs)
{ return lhs.name_ < rhs.name_; }

};

// 2.
ostream& operator<< (ostream& os, const PersonalInfo& pi)
{
os << pi.name() << ' ' << pi.surname() << ' ' << pi.age() << endl;
return os;
}

void dump(vector<PersonalInfo>& vec)
{
copy(vec.begin(), vec.end(), ostream_iterator<PersonalInfo>(cout));
cout << endl;
}
}

void bind03()
{
vector<PersonalInfo> vec;
vec.push_back(PersonalInfo("Little", "John", 30));
vec.push_back(PersonalInfo("Friar", "Tuck", 50));
vec.push_back(PersonalInfo("Robin", "Hood", 40));
dump(vec);

cout << "Default sorting (by name):" << endl;
sort(vec.begin(), vec.end()); // 3.
dump(vec);

cout << "By age:" << endl; // 4.
sort(vec.begin(), vec.end(), boost::bind(
less<unsigned int>(),
boost::bind(&PersonalInfo::age, _1),
boost::bind(&PersonalInfo::age, _2)));
dump(vec);

cout << "By surname:" << endl;
sort(vec.begin(), vec.end(), boost::bind(
less<string>(),
boost::bind(&PersonalInfo::surname, _1),
boost::bind(&PersonalInfo::surname, _2)));
dump(vec);

cout << "Using Lambda, sorting by age:" << endl; // 5.
auto fa = [](PersonalInfo p1, PersonalInfo p2) { return less<unsigned int>()(p1.age(), p2.age()); };
sort(vec.begin(), vec.end(), fa);
dump(vec);

cout << "Using Lambda, sorting by surname:" << endl;
auto fs = [](PersonalInfo p1, PersonalInfo p2) { return less<string>()(p1.surname(), p2.surname()); };
sort(vec.begin(), vec.end(), fs);
dump(vec);
}

1. since we define the operator less-than for this class, it is possible sort its objects
2. let's the ostream know how to print it
3. first call to sort(), using the default less-than operator
4. let's use boost::bind to create a ordering predicate on the fly. We use the STL less functor passing it the two elements in the sequence that the sort algorithm provides
5. same as 4., but using C++0x lambda expressions. It is not necessary storing the lambda expression in a local variable, but it looks to me in this way the code is a bit more readable; and if you use the cool C++0x feature of type inference, by the keyword auto, it is neat and clear.

The code is based on an example provided by "Beyond the C++ Standard Library: An Introduction to Boost", by Björn Karlsson, an Addison Wesley Professional book. An interesting reading indeed.

No comments:

Post a Comment