How would we pass a custom comparator to std::max_element before C++11, say to compare strings by increasing size? Certainly by writing a free or static function taking two strings as arguments and returning the result of the comparison.
// Comparator for strings by increasing size.
// Declared as a free function.
static bool string_size_less_equal
(const std::string& lhs, const std::string& rhs)
{
return lhs.size() < rhs.size();
}
std::size_t
largest_string_size(const std::vector<std::string>& strings)
{
// The comparator is outside the scope of this function.
return
std::max_element
(strings.begin(), strings.end(), &string_size_less_equal)
->size();
}// Need a function object if the comparator has parameters.
struct string_size_equals
{
std::size_t size;
bool operator()(const std::string& string) const
{
return string.size() == size;
}
};
bool has_string_of_size
(const std::vector<std::string>& strings, std::size_t s)
{
string_size_equals comparator = {s};
return
std::find_if(strings.begin(), strings.end(), comparator)
!= strings.end();
}
Now what if we needed a parameterized comparator, for example to call std::find_if to search for a string of a specific size? The free function would not allow to store the size, so we would certainly write a functor object, i.e. a struct storing the size parameter and defining an operator() receiving a string and returning the result of the comparison.
Having the comparators as independent objects or functions is nice if they are used in many places, but for a single use it is undoubtedly too verbose. And confusing too. Wouldn’t it be clearer if the single-use comparator was declared next to where it is used? Hopefully this is something we can use with lambdas, starting with C++11:
bool has_string_of_size
(const std::vector<std::string>& strings, std::size_t s)
{
// Only three lines for the equivalent of the type
// declaration, definition and the instantiation.
// The third argument is a lambda.
return std::find_if
(strings.begin(), strings.end(),
[=](const std::string& string) -> bool
{
return string.size() == s;
})
!= strings.end();
}
Guideline
Mind the next reader: keep your lambdas small.
A declaration like
[a, b, c]( /* arguments */ ) -> T { /* statements */ }struct something
{
T operator()( /* arguments */ ) const { /* statements */ }
/* deduced type */ a;
/* deduced type */ b;
/* deduced type */ c;
};
is equivalent to
Actually the compiler will create a unique type like this struct for every lambda we write.
More conceptually, the parts of a lambda are the following:
[capture](arguments) specifier ->return_type { body }
Where: