This is my learning notes from the course - Mastering C++ Standard Library Features.
Lambdas: Function Objects in Disguise
- How lambda expressions work
- Desugaring lambdas to function objects
- Performance overhead of lambda expressions
Lambdas are just “syntactic sugar” for function objects.
Example: Printing Elements of an std::vector
C++03:
1
2
3
4
5
6
7
8
9
10
11struct printer
{
// overload function call operator()
void operator()(int x) const
{
std::cout << x;
}
};
std::vector<int> v{1, 2, 3, 4, 5};
std::for_each(v.begin(), v.end(), printer());*”The function call operator () can be overloaded for objects of class type. When you overload ( ), you are not creating a new way to call a function. Rather, you are creating an operator function that can be passed an arbitrary number of parameters.”
– from Function Call Operator () Overloading in C++*C++11:
1
2
3
4
5std::vector<int> v{1, 2, 3, 4, 5};
std::for_each(v.begin(), v.end(), [](int x)
{
std::cout << x;
});Desugaring: Simple Lambda
1
[](int x){ std::cout << x; }
is equivalent to
1
2
3
4struct anonymous_lambda_type
{
auto operator()(int x) const { std::cout << x; }
};- Notice that, the
const
qualifier onoperator()
is implicit.- The rules follow auto type deduction. It copies by default, so be careful!
- Notice that, the
Desugaring: Lambda with Return Statement
Lambda Captures
Lambdas can capture variables from the surrounding environment.
1
2
3
4std::vector<int> v{1, 2, 3, 4, 5};
auto& os = std::cout;
std::for_each(v.begin(), v.end(), [&os](int x){ os << x; });Desugaring: Lambda with Capture (by Reference)
1
[&os](int x){ os << x }
is equivalent to
1
2
3
4
5
6
7struct anonymous_lambda_type
{
std::ostream& d_os;
anonymous_lambda_type(std::ostream& os) : d_os(os) { }
auto operator()(int x) const { d_os << x; }
};- The
struct
and its constructor are implicitly generated by the compiler for you.
- The
Variables can be captured either by copy or by reference.
Desugaring: Lambda with Capture (by Copy)
1 | [i]{ std::cout << i; } |
is equivalent to
1 | struct anonymous_lambda_type |
Desugaring: Lambda with Capture (by Reference)
1 | [&i]{ std::cout << i; } |
is equivalent to
1 | struct anonymous_lambda_type |
Performance Overhead
- Every lambda expression produces a unique anonymous type
- There is no type-erasure: the compiler is able to inline aggressively
- Lambda expressions are zero-overhead abstractions
- https://godbolt.org/g/MTX3mW
Summary
- Learned that the Lambda expressions are syntactic sugar for function objects
- The generated function object type is “anonymous”
- Found that it contains a
const
qualifiedoperator()
that returnsauto
- Studied that the captures are stored inside the closure
- Learned that there are member variables of the generated function object type