Page 1 of 1
Lambdas in C++
Posted: Fri Jan 29, 2021 11:28 am
by PeterX
AndrewAPrice mentioned using and appreciating lambdas in C++.
Does one really need lambdas when you have functions? What are they good for, as a function is always more self-explanatory?
Greetings
Peter
Re: Lambdas in C++
Posted: Fri Jan 29, 2021 11:54 am
by kzinti
What difference do you see between a function and a lambda?
Re: Lambdas in C++
Posted: Fri Jan 29, 2021 11:55 am
by PeterX
A lambda is anonymous. A function has a name.
Re: Lambdas in C++
Posted: Fri Jan 29, 2021 11:57 am
by sj95126
I suppose you could make a few arguments: one, if the lambda is specific to its particular use, it's self-contained and localized. Sort of the same way you might declare local variables within a block or loop, rather than at the beginning of a function. Second, it can serve as an implicit definition that the lambda code is inline, which might result in more efficient code. (for example, the compiler might skip the steps to move around the function arguments to comply with the ABI)
Personally, while I appreciate what lambda's do, it's one of those things that's easy to overuse and abuse. If using a lambda means you now have 2-3 lines of code instead of 1, you're probably doing it wrong.
Re: Lambdas in C++
Posted: Fri Jan 29, 2021 11:57 am
by kzinti
Bingo. The lambda function doesn't have a name.
So the only other difference then is that you can't reuse the anonymous lambda since it doesn't have a name.
The main argument for it is that it makes some code more readable and take less lines of code.
But lambdas and normal functions are essentially the same.
Re: Lambdas in C++
Posted: Fri Jan 29, 2021 12:47 pm
by PeterX
kzinti wrote:The main argument for it is that it makes some code more readable and take less lines of code.
Concerning readabilty I think the opposite way. But maybe you are right.
Greetings
Peter
Re: Lambdas in C++
Posted: Fri Jan 29, 2021 12:58 pm
by Schol-R-LEA
The real advantage of lambdas doesn't really apply to C++, IMAO. The real advantage of lambdas is in using them to generate runtime code, and that simply isn't feasible in C++ in the ways it is in, say, Common Lisp or Haskell. Unless you can compile or interpret the code at run time, lambdas don't really help much in general.
Note that lambdas have a number of important roles in both computability theory and compiler theory, and
Scheme is essentially
built out of lambdas (though it was even more so in its original form), but that's separate from the practical uses in a language such as C++.
Also, some other languages which have lambdas use them in specific and limited ways, which make them useful for language-specific purposes - such as
Python, where it is useful in list comprehensions, or
Java, where IIRC they can be used as in lieu of inner functions for callbacks and event handlers.
This isn't to say they have no place in C++; I honestly don't know enough about how they are used there to say. I can certainly see uses for, say, functors (which are functions manipulated at runtime through pointers) and callbacks (where an anonymous functor may be passed as the callback pointer). Perhaps someone such as Solar can speak on it with more detail and authority.
Re: Lambdas in C++
Posted: Fri Jan 29, 2021 1:01 pm
by kzinti
PeterX wrote:Concerning readabilty I think the opposite way. But maybe you are right.
I've always seen people on both sides of this. Which one is more readable ends up being different for each one of us.
Re: Lambdas in C++
Posted: Fri Jan 29, 2021 3:32 pm
by nullplan
Lamdas in C++ are used in places where callback code would be needed so that you don't need to decouple a class or function.
Say you have a collection of structures. You want to find an element of the collection where one data field matches a given parameter. Say, a list of employees and you are looking for some SSN. You can write
Code: Select all
struct Employee {
std::string name;
Department department;
std::string SSN;
/* ... */
};
void do_something_with(std::string SSN)
{
for (auto it = employees.begin(); it != employees.end(); ++it)
if (it->SSN == SSN)
break;
/* ... */
}
But recently, C++ has been on something of a crusade against loops. We've all written more than enough loops in our lives. Now, we could use std::find_if to find the wayward employee, but that requires a predicate. Which we can do like this:
Code: Select all
class matchesSSN {
const std::string &SSN;
public:
explicit matchesSSN(const std::string &SSN) : SSN(SSN) = default;
bool operator()(const Employee& emp) { return emp.SSN == SSN; }
};
void do_something_with(std::string SSN)
{
auto it = std::find_if(employees.begin(), employees.end(), matchesSSN(SSN));
That is better, but it requires us to create a whole new class, just for a single purpose. Reuse of that class is going to be very limited. It is essentially boilerplate. However, with lambdas:
Code: Select all
void do_something_with(std::string SSN)
{
auto it = std::find_if(employees.begin(), employees.end(), [SSN](const Employee& emp){ return emp.SSN == SSN; });
And boom, no more loop and no more temporary class with weird semantics.
You might not see the appeal with find_if, but make it remove_if and suddenly it is useful.
PeterX wrote:Does one really need lambdas when you have functions?
Lamdas can capture. Functions cannot. You can create functors to make a function that captures some state, but for single-use items, this is typically less than ideal.
I would bring up the example of qsort(), where it is always a hassle to uncouple a comparator from the rest of the code, often just for a single use, and how a capture-less lambda can be used there, but then, C++ has std::sort.
Re: Lambdas in C++
Posted: Sun Jan 31, 2021 5:11 am
by AndrewAPrice
Lambdas can make code more readable and flow more sequentually.
There is also a lot of power in capturing closures.
There are three common scenarios I use lambdas in production code: (where I deal with massive amounts of data and latency Is a concern.)
1) I want to iterate over a set, and want to call a handler inline rather than allocate and return collections, especially if it's just used temporarily. E.g. instead of:
Code: Select all
std::vector<Document> FetchInterestingDocuments() {
std::vector<DataStoreFormat> data_store_documents = FetchDocumentsFromBackend();
std::vector<Document> all_documents = ParseDataStoreDocuments(data_store_documents);
std::vector<Document> interesting_documents;
for (const Document &document : all_documents) {
if (IsinterestingDocument(document)) {
interesting_documents.push_back(document);
}
return interesting_documents;
}
You could do:
Code: Select all
void ForEachInterestingDocument(const std:function<void(const Document&>)& on_each_interesting_document) {
ForEachDataStoreDocument([&on_each_interesting_document] (const DataStoreDocument& data_store_document) {
Document document = ParseDataStoreDocument(data_store_document);
if (IsInterestingDocument(document)) {
on_each_interesting_document(document);
}
}
}
2) I'm building a function, especially one that will execute many times (e.g. for every 100,000 items in the data store) and I can do some precalculation and pass it into the lambda, e.g.
Code: Select all
std::function<bool(const Document&)> BuildIsInterestingDocumentFunction(const Request& request) {
StructuredQuery structured_query = ParseRawQuery(request.RawQuery());
switch (structured_query.Type()) {
case SIMPLE_QUERY: {
SimpleQueryEvaluator evaluator(structured_request);
return [evaluator](const Document& document) {
return evaluator.IsInterestingDocument(document);
}
case COMPLEX_QUERY: {
ComplexQueryEvaluator evaluator(structured_request);
return [evaluator](const Document& document) {
return evaluator.IsInterestingDocument(document);
}
default:
return [](const Document&) { return false; };
}
}
Now can call BuildIsInterestingDocumentFunction once per server request but call the returned function for each of the hundreds of thousands of documents.
3) To put handing/callbacks inline, especially when they are small, if it increases readability by avoiding the reader from having to scroll up and down the file to see what some small, trivial handler does.
----
As in all things programming, there are multiple ways to do things and you can avoid lambdas. I use lambdas if it helps readability. I hate overly verbose code (e.g. the Java practice of making every handler a class and putting it into its own file.) I like code that reads sequentially.