A lambda expression is a mechanism for specifying a function. The primary use for a lambda is to specify a simple action. A lambda expression can be implemented directly as anonymous function, for example within an algorithm method, or it can be named and used by its name.
Of course, there are already some other possibilities to implement functions. Therefore, we may think about the question when we should use lambdas and when we should not use them. Furthermore, we have to decide between named and anonymous lambdas. Following I will give you some guidelines which will help do find a decision.
This comparison is based on a simple use case: we want to sort a list of signed integers by looking at the absolute values and we want to sort in descending order. There exists an STL algorithm for sorting. So, we use this algorithm and provide a comparison function for two elements. At first, we implement this function by using a functor.
struct AbsoluteDescendingComparer { bool operator()(int a, int b) { return abs(a) > abs(b); } }; int _tmain(int argc, _TCHAR* argv[]) { auto data = std::vector {-5, 1, 15}; std::sort(data.begin(), data.end(), AbsoluteDescendingComparer()); return 0; }
At next we implement the same function by using an anonymous lambda. This will give us the possibility to write the comparison directly within the sort algorithm and therefore we can create very short and compact source code.
int _tmain(int argc, _TCHAR* argv[]) { auto data = std::vector {-5, 1, 15}; std::sort(data.begin(), data.end(), [](int a, int b) { return abs(a) > abs(b); } ); return 0; }
Another possible implementation will be a named lambda. We can create this lambda first and use it later within the algorithm.
int _tmain(int argc, _TCHAR* argv[]) { auto data = std::vector {-5, 1, 15}; auto AbsoluteDescendingComparer = [](int a, int b) { return abs(a) > abs(b); }; std::sort(data.begin(), data.end(), AbsoluteDescendingComparer); return 0; }
Comparison of the implementations
If we now ask our self which of the three alternatives we prefer, I’m sure, most will say: the anonymous lambda. That’s because it allows to write the code in a very compact way without any overhead. But what if we ask about our preference in case we have to maintain code? In this case the decision may not that easy anymore. The anonymous lambda has one major disadvantage: to leave out the function name will result in a loss of information. The function name, if it is chosen wisely, offers a lot of information about the implemented function. Without this name, we have to understand the implementation itself to know what it is used for. In case we use an anonymous lambda we have to add a comment to explain the implementation.
Another issue which decreases the code readability and makes it more difficult to understand and maintain the code, is based on the mixing of the two concerns. We have the sort-algorithm and we have a comparer. By using a named lambda we have a clear separation of concerns. Instead, if we use an anonymous lambda, these two functionalities will be mixed together.
The separation of concerns will become even more importand in case the function itself becomes more complex. In such a case, an anonymous lambda as well as an named one are not suitable anymore because both are placed in the local scope of the including method. If such a method becomes too complex it should be refactored by extracting code into separate methods. Therefore, complex functionality should not be implemented by using a lambda.
Recommendations
For a simple functionality, needed in local scope only, a lambda can be used. Otherwise use a functor, a class member function or a non-member function. If you use a lambda I would recommend preferring the named lambda. An anonymous one should only be used if the functionality is simple enough to understand it in the same time it needs to understand the meaning of a function name.
Pingback: Lambda Closures in C++ | coders corner