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(); }
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.
// 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(); }
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? Thankfully this is something we can do 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 */ }
is equivalent to
struct something { T operator()( /* arguments */ ) const { /* statements */ } /* deduced type */ a; /* deduced type */ b; /* deduced type */ c; };
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: