Reputation: 187
I understood that a predicate takes argument from an input range of iterators and does operations as intended, but when we supply a binary predicate and a pair of iterators, how does it work? How are the elements of a container passed to the predicate?
E.g. std::sort
sorts a vector of strings alphabetically by default, but to sort them according to sizes we can pass a predicate like:
sort(vec.begin(), vec.end(), [](const string &a, const string &b) {
return a.size() < b.size();
});
So, this is supposed to sort the vector of strings vec
by size and not alphabetically, but I can't understand how this is happening, i.e. how is the predicate taking elements of the vector as arguments and how is it sorting?
Upvotes: 2
Views: 1534
Reputation: 4078
When std sort is called, the "normal" version of this function will perform some comparison using operator<
. Like you understood, the comparison between two string is lexicographic (like a dictionary).
However, people that developed the std::sort
function think about cases like :
operator<
Here it is where the predicate is useful. The predicate (like Igor Tandetnik says) is called inside the std::sort
function.
So, with the normal version, std::sort
will do something like that :
if(*a < *b) {
...
}
In the predicate version, it will do almost the same :
if(predicate(*a, *b)) {
...
}
Going that way, your predicate replace the operator<
function.
Upvotes: 2
Reputation: 899
A binary predicate is nothing else than a callable object (including a function or lambda) that expects two parameters of the type of the dereferenced iterators of the container. Its "role" is to provide a comparison criteria for the sort algorithm.
The std::sort
algorithm expects that this function or function object returns a value that is convertible to bool and which yields true, if and only if the first argument is before the second argument in the sort order (so-called strict weak order). The std::sort
algorithm then operates (sorts) on the given range of objects (begin to end of container) by applying the predicate to elements when comparing the elements individually.
In your case, if the container is a vector of strings, the dereferenced iterators yield std:string
and thus your lambda works fine.
Here is some example code showing how it works with a lambda that is independent of the concrete type, but requires at least the size()
operation to be defined on the objects in the container. It also shows, how the binary predicate could be provided as a function object (in which case I reverted the sort order by inverting the predicate):
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using Words = std::vector<std::string>;
struct CompareReverse
{
bool operator() (auto lhs, auto rhs)
{
return rhs.size() < lhs.size(); // less-than reversed!
}
};
int main()
{
Words words { "red", "green", "blue", "wizard", "a", "letter", "very-long-word" };
std::sort(words.begin(), words.end(), [](auto a, auto b) { return a.size() < b.size(); });
for (auto w : words)
std::cout << w << std::endl;
std::sort(words.begin(), words.end(), CompareReverse());
for (auto w : words)
std::cout << w << std::endl;
return 0;
}
Upvotes: 1