This document discusses lambda expressions in C++. It begins with a brief introduction to lambdas, noting that they are unnamed functions that can access enclosing scopes and have an unspecified type. It then covers lambda syntax including capture lists, arguments, return types, and invocations. Examples are provided to demonstrate capturing values by copy or reference. Advantages of using lambdas like code compacting and algorithm specialization are presented. The document also discusses passing lambdas using std::function and templates. It concludes by asking for additional use cases and any questions.
3. About me
Dimitrios Platis
● Grew up in Rodos, Greece
● Software Engineer @ Zenseact, Sweden
● Course responsible @ DIT112, DAT265
● C++ courses
○ Beginner
○ Advanced
○ Software Design & Architecture
● Interests:
○ Embedded systems
○ Open source software & hardware
○ Robots, Portable gadgets, IoT, 3D printing
● Meetups
○ Embedded@Gothenburg
○ grcpp
● Website: https://platis.solutions
4. What is your hometown?
ⓘ Start presenting to display the poll results on this slide.
5. Agenda
● Brief intro
● Basic syntax
● Use cases
⚠ Disclaimers ⚠
- Treat code as pseudocode
- Assuming C++20
- Not (m)any C++20-specific features
demonstrated
- Most, if not everything, should be
C++14 compatible
- Not following any specific coding
guidelines
- Assuming no domain restrictions
- There may be room for optimization
6. Lambdas - λ
● Unnamed functions, inspired from
functional programming
● May have access to the enclosing
scope
● Unspecified type
● Easy/cheap to create
● Less boilerplate than writing a
function
● "Exotic" syntax
● C++ Lambdas under the hood
auto grcpp = [ ] ( ) { learn_lambdas(); };
grcpp();
[ ] 👉 Capture nothing
( ) 👉 No arguments
{ learn_lambdas(); } 👉 What to do
grcpp() 👉 Invoke lambda
7. [<capture list>] (<arguments>) -> <return type> {
<function body> }
std::string generation{};
if (yearBorn < 1964)
{
generation = "boomer";
}
else if (yearBorn < 1980)
{
generation = "generation x";
}
else if (yearBorn < 1999)
{
generation = "millenial";
}
else
{
generation = "zoomer";
}
auto getGeneration = [](int yearBorn) -> std::string {
if (yearBorn < 1964)
{
return "boomer";
}
else if (yearBorn < 1980)
{
return "gen x";
}
else if (yearBorn < 1999)
{
return "millenial";
}
return "zoomer";
};
const auto generation = getGeneration(yearBorn);
☝ What are the advantages of this?
8. Capture by value
auto getGeneration = [yearBorn]() -> std::string
{
if (yearBorn < 1964)
{
return "boomer";
}
else if (yearBorn < 1980)
{
return "generation x";
}
else if (yearBorn < 1999)
{
return "millenial";
}
return "zoomer";
};
const auto generation = getGeneration();
💡 Take yearBorn from the enclosing scope
by copy and use it internally
9. Capture by reference
💡 Take generation by reference and use it
internally.
std::string generation{};
auto getGeneration = [&generation](int yearBorn) -> void {
if (yearBorn < 1964)
{
generation = "boomer";
}
else if (yearBorn < 1980)
{
generation = "generation x";
}
else if (yearBorn < 1999)
{
generation = "millenial";
}
generation = "zoomer";
};
getGeneration(yearBorn);
10. Capture clauses
[ ] Capture nothing
[ = ] Capture everything by value
[ & ] Capture everything by reference
[ foo ] Capture foo by value
[ &foo ] Capture foo by reference
[ this ] Capture this (class)
[ =, &foo] Capture everything by value and foo by
reference
[ foo = v[0], &bar = v[1] ] Capture v[0] as foo by value and v[1] as bar
by reference
11. Trailing return type (optional)
auto getGeneration = [](int yearBorn) {
if (yearBorn < 1964)
{
return "boomer";
}
else if (yearBorn < 1980)
{
return "generation x";
}
else if (yearBorn < 1999)
{
return "millenial";
}
return "zoomer";
};
const auto generation = getGeneration(yearBorn);
⚠ If you omit the return type then its auto
🤔 What is the type of generation?
13. Invoke directly
💡 It may be convenient to invoke a
lambda directly, but consider if this
decreases readability.
const auto generation = [](int yearBorn) -> std::string {
if (yearBorn < 1964)
{
return "boomer";
}
else if (yearBorn < 1980)
{
return "gen x";
}
else if (yearBorn < 1999)
{
return "millenial";
}
return "zoomer";
}(yearBorn);
14. Under the hood: Functors
class NameGetter
{
public:
NameGetter (int id, std::string& name) : mId{id}, mName{name}
{}
bool operator() (std::map<int, std::string> data)
{
if (!data.contains (mId)) { return false; }
mName = data.at(mId);
return true;
}
private:
int mId;
std::string& mName;
};
NameGetter getNameFunctor{id, name};
auto functorResult = getNameFunctor(data);
auto getNameLambda = [id, &name](auto data) {
if (!data.contains(id))
{
return false;
}
name = data.at(id);
return true;
};
auto lambdaResult = getNameLambda(data);
15. Full syntax 🚀
● A lot of optional specifiers
○ mutable
○ constexpr
○ throw
○ template parameter types
● Further reading on cppreference
16. How "strange" do you find
the lambda syntax?
ⓘ Start presenting to display the poll results on this slide.
17. Compacting complex expressions
● The car should stop moving if a valid sensor measurement
places an obstacle closer than 3 meters
● A measurement is valid if it's larger than 0
if (car.getSpeed() > 0)
{
const auto distance = frontSensor.getDistance();
if (distance > 0 && distance < 3) { car.stop(); }
}
else if (car.getSpeed() < 0)
{
const auto distance = rearSensor.getDistance();
if (distance > 0 && distance < 3) { car.stop(); }
}
auto isObstacle = [](auto distance) {
return distance > 0 && distance < 3;
};
if (car.getSpeed() > 0 &&
isObstacle(frontSensor.getDistance()))
{
car.stop();
}
else if (car.getSpeed() < 0 &&
isObstacle(rearSensor.getDistance()))
{
car.stop();
}
18. Specializing algorithms
std::for_each(ages.begin(), ages.end(), [](auto age) {
// Do something with age
});
std::map<std::string, int> members{{"John", 31}, {"Jane", 29}, {"Mike", 17}};
auto underage = std::find_if(members.begin(), members.end(), [](auto member)
{
return member.second < 18;
});
if (underage != members.end())
{
std::cout << "Underage member: " << underageMember->first << std::endl;
}
😮 Think lambda whenever you see
Predicate, Function, Deleter etc as
an input!
● std::thread
● std::condition_variable::wait_for
● std::unique_ptr
● …and more
19. How to pass a lambda - std::function
// class ButtonController
private:
std::map<int, std::function<void(Action)>> listeners_;
public:
void setListener(int buttonId,
std::function<void(Action)> listener)
{
listeners_[buttonId] = listener;
}
void click(int buttonId, Action action)
{
if (listeners_.contains(buttonId))
{
listeners_[buttonId](action);
}
}
auto loggingButtonlistener = [this](Action action) {
switch (action)
{
case Action::MouseDown:
startLogging();
break;
case Action::MouseUp:
stopLogging();
break;
default:
break;
};
};
buttonController_->setListener(loggingButtonId,
loggingButtonlistener);
20. How to pass a lambda - Templates &
function pointers
Templates
template <typename Function>
void runRepeatedly(Function f)
{
int iteration{ 0 };
while (f(iteration))
{
iteration++;
// Do some more stuff
}
}
Function pointers (capture clause empty)
void runRepeatedly(bool (*f)(int))
{
int iteration{ 0 };
while (f(iteration))
{
iteration++;
// Do some more stuff
}
}
21. GoogleMock
struct UpdaterMock
{
MOCK_METHOD((bool), update, (std::vector<int>&), ());
};
// With lambdas
std::vector<int> initial{};
EXPECT_CALL(updater, update(_)).WillOnce([&initial](auto& data) {
initial = data;
data.at(0) = 55;
return true;
});
// Without lambdas
EXPECT_CALL(updater, update(_))
.WillOnce(
DoAll(SaveArg<0>(&initial), SetArgReferee<0>(/* ?! */), Return(true)));
● No need to use GMock macros
○ DoAll
○ SaveArg
○ SetArgReferee
○ InvokeWithoutArgs
○ …many more!
● Setting actions to be triggered on
mocked method execution becomes
easier and more readable
22. Do you have another
use case for lambdas in
mind?
Any questions?
23. How much did you enjoy
this workshop?
ⓘ Start presenting to display the poll results on this slide.
24. JetBrains lottery
1 year license for any Jetbrains IDE!
1. Go to: http://plat.is/jetbrains
2. If you won, please stick around until I
contact you
3. If you did not win, better luck next time!
Did you know that as a university student you
can get a free JetBrains license anyway?