Daily Dose C++: Lambdas (Part 2)
October 1, 2020•556 words
Source
Back To Basics: Lambda Expressions - Barbara Geller & Ansel Sermersheim - CppCon 2020
Starting from 31:50
C++20 Update
Lambda Expression Format:
[capture clause] <template parameters> (parameter list) specifier exception attribute -> return type requires {body}
specifier
keyword:- mutable (C++11)
- constexpr (C++17)
- Deduced so keyword is optional.
- Only reason, lambda express NEEDS to be constexpr.
- Can constexpr be using in a lambda expression?
- Yes, only if lambda is a generic type. Only in the body.
- consteval (C++20)
- similar to how const expr does in lexp
exception
keyword:- noexcept: cannot throw an exception if noexcept keyword used in the
exception
spot of the l-expression format above.
- noexcept: cannot throw an exception if noexcept keyword used in the
attribute
keyword:- Function attributes are not supported in lambda expressions.
nodiscard
,deprecated
,noreturn
- Function type appears at the end of declaration,
gnu::cdecl
,gnu::regcall
.- This is compiler specific.
- Function attributes are not supported in lambda expressions.
requires
keyword:- Lambda Expressions need to be generic
- e.g.
requires std::copyable\<T>
What's so great?
- Easier to read
- Convenient to write than a function object
- Invoked immediately and not saved to a variable
- Easier to use with algorithms and std::visit(), std::thread.
- Designed well to work with those items.
- Instead of writing a whole class and code, you just write the important parts and the compilers takes care of the rest.
- Lambda expressions described as kind of backwards.
Callback
- Can be passed as a function pointer, f obj, or closure.
- Callback will be invoked by the receiver.
// Callback with STL Algorithms : std::count_if
int resB = std::count_if(data.begin(), data.end(), [](int i){return i>5;});
Maps
auto myLamb = [](const std::string &a, const std::string &b)
{return a.size() > b.size();};
std::map<std::string, int, decltype(myLamb)> myMapB =
{{string name, int size}, {"e.g.NameHere", 10}, ...}
Have to define as decltype and passed lambda expression as an auto decltype.
Generic Lambda
If auto is used for parameters declaration is what defines a generic lambda.
At least 1 parameter is defined as auto, then the whole lambda expression is generic.
auto myLamb = [](){}
This auto is to define the closure type, not the auto defining the generic.
datatype myLamb = [](auto var1, int var2){}
This auto is the one that defines a generic.
Capturing Structured Bindings
auto [x, y] = someFunction(); // Structured Bindings
auto myLamb = [x](){return x+7;} // Capturing x from Structured Binding
Technically illegal C++17. But some compilers will allow it.
If it fails to build, work around is to just set the capture clause as [x = x]
.
Resolved in C++20, but some compilers might not be up to date with this fix.
Extra Notes
No recursive lambdas, can't call itself.
C++11 Lambdas is more of a beta release. Preferred to be used in at least C++14.
Function pointers are more expensive to call than function object.
Beneficial if you capture the
std::move(myPtr)
and place it into a variable of a different name.- I'm guessing this is to reduce confusion. Since the value will no longer exist in the old variable.
- Common 'gotcha' you need to be wary of.
Also related to the move action, be careful about mutable lambdas.
- Try to use movable captures over mutable lambda expressions.