Module 52
Module 52
L
Partha Pratim
Das
Programming in Modern C++
E
Objectives &
Outlines
Module M52: C++11 and beyond: General Features: Part 7: Lambda in C++/1
T
λ in C++
Syntax and
Semantics
P
Closure Object
Lambdas vs.
Closures
Partha Pratim Das
N
FCO
Anatomy
Parameters
Department of Computer Science and Engineering
Capture
Indian Institute of Technology, Kharagpur
By Reference[&]
By Value [=]
Mutable
[email protected]
Restrictions
Practice Examples
All url’s in this module have been accessed in September, 2021 and found to be functional
Module Summary
Module M52
• Learnt how Rvalue Reference works as a Universal Reference under template type
L
Partha Pratim
Das
deduction
E
Objectives &
Outlines • Understood the problem of forwarding of parameters under template type deduction
and its solution using Universal Reference and std::forward
T
λ in C++
Syntax and
Semantics
• Learnt the implementation of std::forward
P
Closure Object
Lambdas vs.
Closures • Understood how Move works as an optimization of Copy
N
FCO
Anatomy
Parameters
Capture
By Reference [&]
By Value [=]
Mutable
Restrictions
Practice Examples
Module Summary
Module M52
L
Partha Pratim
Das
◦ Closure Objects
E
Objectives &
Outlines ◦ Parameters
◦ Capture
T
λ in C++
Syntax and
Semantics
P
Closure Object
Lambdas vs.
Closures
N
FCO
Anatomy
Parameters
Capture
By Reference [&]
By Value [=]
Mutable
Restrictions
Practice Examples
Module Summary
Module M52
L
Partha Pratim
Das 1 λ in C++11, C++14, C++17, C++20
Syntax and Semantics
E
Objectives &
Outlines Closure Object
T
λ in C++ Lambdas vs. Closures
Syntax and
Semantics
First Class Object
P
Closure Object Anatomy
Lambdas vs.
Closures Parameters
N
FCO
Anatomy
Capture
Parameters By Reference [&]
Capture By Value [=]
By Reference [&]
Mutable
By Value [=]
Mutable
Restrictions
Restrictions Practice Examples
Practice Examples
Module Summary
2 Module Summary
Module M52
Source:
•
L
Partha Pratim
Das
Lambdas, isocpp.org
• Scott Meyers on C++
• Lambda capture, cppreference.com
E
Objectives &
Outlines • Lambdas: From C++11 to C++20, Part 1 and Lambdas: From C++11 to C++20, Part 2, cppstories.com, 2019
• Lambdas: Smart Pointers, Jim Fix, Reed College
T
λ in C++
Syntax and
Semantics
P
Closure Object
Lambdas vs.
Closures
N
FCO
Anatomy
Parameters
Capture
By Reference [&]
By Value [=]
Mutable
Restrictions
Practice Examples λ in C++11, C++14, C++17, C++20
Module Summary
Module M52
• A λ expression is a mechanism for specifying a function object or functor (Recall Module 40)
• The primary use for a λ is to specify a simple action to be performed by some function
L
Partha Pratim
Das
• For example, consider a remainder operation rem that computes m % n, that is, m modulo n.
E
Objectives &
Outlines
It has type int -> int. To write rem in C++, we define a function / functor:
T
λ in C++
int n = 7; int n = 7;
Syntax and
Semantics int rem(int m) // Function struct remainder { // Functor
P
Closure Object
{ return m % n; } int mod; // State
Lambdas vs.
Closures // Uses n in context remainder(int n): mod(n) { } // Ctor (n from context)
N
FCO int operator()(int m) // Function call operator
Anatomy
Parameters
{ return m % mod; } // Body
Capture };
By Reference [&]
By Value [=] struct remainder rem(n);
Mutable
rem(23); // 2 rem(23); // 2
Restrictions
Practice Examples
Module Summary
λ: auto rem = [n](int m) -> int { return m % n; } // Captures n from context
rem(23); // 2
• Note that [n] Captures n from context to close rem and create the Closure Object in C++
Programming in Modern C++ Partha Pratim Das M52.6
C++ λ’s
Module M52
• C++11 introduced λ’s as syntactically lightweight way to define functions on-the-fly
• λ’s can capture (or close over) variables from the surrounding scope – by value or by reference
L
Partha Pratim
Das
• First consider callable things that do not capture any variables. C++ offers three alternatives:
E
Objectives &
Outlines ◦ plain functions (All versions of C & C++)
◦ functor classes (C++03 onwards), and
T
λ in C++
Syntax and
Semantics
◦ lambdas (C++11 onwards)
P
Closure Object
Lambdas vs.
Closures #include <iostream> // cout
N
FCO using namespace std;
Anatomy
Parameters
Capture
int function (int a) { return a + 3; }
By Reference[&] class Functor { public: int operator()(int a) { return a + 3; } };
By Value [=] auto lambda = [] (int a) { return a + 3; };
Mutable
Restrictions
Practice Examples int main() { Functor functor;
Module Summary cout << function(5) << ' ' << functor(5) << ' ' << lambda(5) << endl;
}
8 8 8
• For plain functions that capture no variables, lambdas and functors behave the same
Programming in Modern C++ Partha Pratim Das M52.7
C++ λ Syntax and Semantics
Module M52
L
Partha Pratim
Das
[capture list] (parameter list) -> return-type { function body }
E
Objectives &
Outlines • The capture list and parameter list can be empty, so the following is a valid λ:
T
λ in C++
Syntax and []() { cout << "Hello, world!" << endl; }
Semantics
P
Closure Object • Parameter list is a sequence of parameter types and variable names as for an ordinary function
Lambdas vs.
Closures • Function body is like an ordinary function body
N
FCO
Anatomy
• If the function body has only one return statement (which is very common), the return type is
Parameters assumed to be the same as the type of the value being returned
Capture
By Reference[&]
• If there is no return statement in the function body, the return type is assumed to be void
By Value [=]
◦ Below λ has return type void – can be called without any use of the return value:
Mutable
Restrictions []() { cout << "Hello from trivial lambda!" << endl; } ();
Practice Examples
Module Summary
◦ However, trying to use the return type of the call is an error:
cout << []() { cout << "Hello from trivial lambda!" << endl; } () << endl;
Module M52
• Below λ returns a bool value which is true if the first param is half of the second. The
compiler knows the return type as bool from the return statement:
L
Partha Pratim
Das
if ([](int i, int j) { return 2 * i == j; } (12, 24))
E
Objectives &
Outlines cout << "It’s true!";
else
T
λ in C++
Syntax and
Semantics
cout << "It’s false!";
P
Closure Object
Lambdas vs.
• To specify return type:
Closures
cout << "This lambda returns " << [](int x, int y) -> int {
N
FCO
Anatomy if(x > 5) return x + y;
Parameters
Capture
else
By Reference [&] if (y < 2) return x - y; else return x * y;
By Value
Mutable
[=]
} (4, 3) << endl;
Restrictions • Below λ, returns an int, though the return statement provides a double:
Practice Examples
Module Summary
cout << "This lambda returns " <<
[](double x, double y) -> int { return x + y; } (3.14, 2.7) << endl;
The output is "This lambda returns 5"
Programming in Modern C++ Partha Pratim Das M52.9
C++ λ: Syntax and Semantics
Module M52
L
Partha Pratim
Das
int n = 7;
E
Objectives &
Outlines
auto rem = [n](int m) -> int { return m % n; };
◦ n is captured by [n] (value – a copy is made from the context) at the time of constructing
T
λ in C++
Syntax and
Semantics
the closure object. Hence n must be initialized before the construction of the closure
P
Closure Object ◦ The value of n cannot be changed within the λ (for immutable λ’s)
Lambdas vs.
Closures ◦ The changes to n after the construction of the closure object are not reflected
N
FCO
Anatomy
• Below λ captures s by reference to accumulate the value of m:
Parameters
Capture
int s = 0;
By Reference [&] auto acc = [&s](int m){ s += m; };
By Value [=]
Mutable
◦ s is captured by [&s] (reference – a reference is set to the context) at the time of
Restrictions constructing the closure object. Hence it is optional to initialize s before the construction
Practice Examples
of the closure. However, it must be initialized before the use of the closure
Module Summary
◦ The value of s can be changed within the λ
◦ The changes to s after the construction of the closure object will be reflected
Programming in Modern C++ Partha Pratim Das M52.10
Lambdas vs. Closures
Module M52
• Closure
L
Partha Pratim
Das ◦ A closure (lexical / function closure), is a technique for implementing lexically scoped name
binding in a language with first-class functions
E
Objectives &
Outlines ◦ Operationally, a closure is a record storing a function together with an environment
◦ The environment is a mapping associating (binding) each free variable of the function with
T
λ in C++
Syntax and
Semantics
the value or reference to which the name was bound when the closure was created
P
Closure Object ◦ Unlike a plain function, a closure allows the function to access captured variables through
Lambdas vs.
Closures the closure’s copies of their values or references, even in its invocations outside their scope
N
FCO
Anatomy
• Lambdas vs. Closures (From Lambdas vs. Closures by Scott Meyers, 2013)
Parameters
Capture
◦ A λ expression auto f = [&](int x, int y){ return fudgeFactor * (x + y); };
By Reference [&] exists only in a program’s source code. A lambda does not exist at runtime
By Value
Mutable
[=]
◦ The runtime effect of a λ expression is the generation of an object, called closure
Restrictions ◦ Note that f is not the closure, it is a copy of the closure. The actual closure object is a
Practice Examples
temporary that’s typically destroyed at the end of the statement
Module Summary
◦ Each λ expression causes a unique class to be generated (during compilation) and also
causes an object of that class type – a closure – to be created (at runtime)
◦ Hence, closures are to lambdas as objects are to classes
Programming in Modern C++ Partha Pratim Das M52.11
Closure Objects: Implementing λ’s
Module M52
• A λ-expression generates a Closure Object at run-time
• A closure object is temporary
L
Partha Pratim
Das
• A closure object is unnamed
E
Objectives &
Outlines
• For a λ-expression, the compiler creates a functor class with:
◦ data members:
T
λ in C++
Syntax and
Semantics . a value member each for each value capture
P
Closure Object
Lambdas vs.
. a reference member each for each reference capture
Closures
◦ a constructor with the captured variables as parameters
N
FCO
Anatomy . a value parameter each for each value capture
Parameters
Capture . a reference parameter each for each reference capture
By Reference
By Value [=]
[&]
◦ a public inline const function call operator() with the parameters of the lambda as
Mutable parameters, generated from the body of the lambda
Restrictions
Practice Examples
◦ copy constructor, copy assignment operator, and destructor
Module Summary • A closure object is constructed as an instance of this class and behaves like a function object
• A λ-expression without any capture behaves like a function pointer
Source: C++ Lambda Under the Hood, 2019
Programming in Modern C++ Partha Pratim Das M52.12
Closure Objects: Implementing λ’s: Example
Module M52
#include <iostream> // lambda & closure object #include <iostream> // Possible functor by compiler
using namespace std; using namespace std;
int main() { int main() {
L
Partha Pratim
Das int val = 0; // for value capture init. must int val = 0; // for value capture init. must
int ref; // for ref. capture init. opt. int ref; // for ref. capture init. opt.
E
Objectives &
Outlines
auto check = [val, &ref](int param){ struct check_f { // functor to show captured values
T
λ in C++
cout << "val = " << val << ", "; int val_f; // value member for value capture
Syntax and
Semantics
cout << "ref = " << ref << ", "; int& ref_f; // ref. member for ref. capture
P
Closure Object cout << "param = " << param << endl; check_f(int v, int& r): // Ctor with
Lambdas vs. }; val_f(v), ref_f(r) { } // value & ref params
Closures
// lambda to show captured values void operator()(int param) const { // param
N
FCO
// constructed with value capture of val cout << "val = " << val_f << ", ";
Anatomy
// and reference capture of ref cout << "ref = " << ref_f << ", ";
Parameters
Capture
// Also, has a parameter param cout << "param = " << param << endl;
By Reference [&] };
By Value [=] };
Mutable auto check = check_f(val, ref); // Instantiation
Restrictions
Practice Examples ref = 2; // init. will be reflected ref = 2; // init. will be reflected
Module Summary check(5); // val = 0, ref = 2, param = 5 check(5); // val = 0, ref = 2, param = 5
val = 3; // change will not be reflected val = 3; // change will not be reflected
check(5); // val = 0, ref = 2, param = 5 check(5); // val = 0, ref = 2, param = 5
ref = 4; // change will be reflected ref = 4; // change will be reflected
check(5); // val = 0, ref = 4, param = 5 check(5); // val = 0, ref = 4, param = 5
}
Programming in Modern C++ } Partha Pratim Das M52.13
Closure Objects: First Class Objects (FCOs)
L
Partha Pratim trace(trace const &) { std::cout << "copy construct\n"; }
Das
~trace() { std::cout << "destroy\n"; }
trace& operator=(trace&) { std::cout << "assign\n"; return *this; }
E
Objectives &
Outlines };
Code Snippets Outputs
T
λ in C++
Syntax and { trace t; // t not used so not captured construct
Semantics
int i = 8; destroy
P
Closure Object
auto m1 = [=](){ return i / 2; };
Lambdas vs.
Closures }
N
FCO
Anatomy { trace t; // capture t by value construct
Parameters auto m1 = [=](){ int i = t.i; }; copy construct Closure object has
Capture std::cout << "-- make copy --" << std::endl; - make copy -
By Reference[&] auto m2 = m1; copy construct implicitly-declared
By Value [=]
} destroy copy constructor /
Mutable
destroy
Restrictions
Practice Examples destroy destructor
Module Summary
{ trace t; // capture t by reference construct
auto m1 = [&](){ int i = t.i; }; -- make copy --
std::cout << "-- make copy --" << std::endl; destroy
auto m2 = m1;
}
Programming in Modern C++ Partha Pratim Das M52.14
Closure Objects: Anatomy
Module M52
L
Partha Pratim
Das
[1] Capture Clause (introducer)
E
Objectives &
Outlines [2] Parameter List (Opt.) (declarator)
[3] Mutable Specs. (Opt.)
T
λ in C++
Syntax and
Semantics [4] Exception Specs. (Opt.)
P
Closure Object
Lambdas vs.
[5] (Trailing) Return Type (Opt.)
Closures
[6] λ body
N
FCO
Anatomy
Parameters
Capture
By Reference [&]
By Value [=]
λ Expression:: E ` my mod : Int, λ(v : Int). v % my mod : Int
Mutable Closure Object:: [my mod](int v) -> int { return v % my mod; }
Restrictions
Practice Examples
Module Summary
• Introducer: [my mod] • Mutable Spec: Skipped
• Capture: my mod • Exception Spec: Skipped
• Parameters: (int v) • Return Type: -> int
• Declarator: (int v) -> int • λ Body: { return v % my mod; }
Programming in Modern C++ Partha Pratim Das M52.15
Closure Objects: Parameters
L
Partha Pratim
Das
[](int v){ std::cout << v << "*6=" << v*6 << std::endl;}(7); 7*6=42
E
Objectives &
Outlines int i = 7;
[](int & v){ v *= 6; } (i);
T
λ in C++
Syntax and std::cout << "the correct value is: " << i << std::endl; the correct value is: 42
Semantics
P
Closure Object
int j = 7;
Lambdas vs.
Closures
[](int const & v){ v *= 6; } (j); // error:
N
FCO std::cout << "the correct value is: " << j << std::endl; // assignment of read-only reference ’v’
Anatomy
Parameters int j = 7;
Capture [](int v){ v *= 6; std::cout << "v: " << v << std::endl;}(j); v: 42
By Reference[&]
By Value [=] int j = 7; // lambda parameters do not affect
Mutable
[](int & v, int j) { v *= j; } (j, 6); // the namespace
Restrictions
std::cout << "j: " << j << std::endl; j: 42
Practice Examples
Module Summary [] std::cout << "foo" << std::endl; (); is same as // lambda expression without a
[]() std::cout << "foo" << std::endl; (); // declarator acts as if it were ()
Module M52 • The captures is a comma-separated list of zero or more captures, optionally with default
• The capture list defines the outside variables that are accessible from the λ function body
L
Partha Pratim
Das
• The only capture defaults are
E
Objectives &
Outlines
◦ [&] (implicitly capture the used automatic variables by reference) and
◦ [=] (implicitly capture the used automatic variables by copy / value)
T
λ in C++
Syntax and
Semantics
• The current object (*this) can be implicitly captured if either capture default is present
P
Closure Object • If implicitly captured, it is always captured by reference, even for [=]. Deprecated since C++20
Lambdas vs.
Closures Capture Meaning C++
N
FCO
Anatomy
identifier simple by-copy capture C++11
Parameters identifier ... simple by-copy capture that is a pack expansion C++11
Capture identifier init by-copy capture with an initializer C++14
By Reference [&]
& identifier simple by-reference capture C++11
By Value [=]
Mutable
& identifier ... simple by-reference capture that is a pack expansion C++11
Restrictions & identifier init by-reference capture with an initializer C++14
Practice Examples this simple by-reference capture of the current object C++11
Module Summary *this simple by-copy capture of the current object C++17
... identifier init by-copy capture with an initializer that is a pack expansion C++20
& ... identifier init by-reference capture with an initializer that is a pack expansion C++20
Source: Lambda capture, cppreference.com
Programming in Modern C++ Partha Pratim Das M52.17
Closure Objects: Capture
Module M52
L
Partha Pratim
Das
◦ Default all by reference
E
Objectives &
Outlines [&](){ ... }
T
λ in C++
Syntax and
◦ Default all by value
Semantics
[=](){ ... }
P
Closure Object
Lambdas vs.
Closures ◦ List of specific identifier(s) by value or reference and/or this
N
FCO
Anatomy [identifier](){ ... }
Parameters
Capture
[&identifier](){ ... }
By Reference [&] [foo,&bar,gorp](){ ... }
By Value [=]
Mutable ◦ Default and specific identifiers and/or this
Restrictions
Practice Examples [&,identifier](){ ... }
Module Summary [=,&identifier](){ ... }
Source: Lambda capture, cppreference.com
L
Partha Pratim
Das
typedef int (*l1) (int); // Function pointer
const l1 f = [](int i){ return i; };
E
Objectives & // Converts to a func. ptr. w/o capture
Outlines
const auto l2 = [=]() { return x; }; // All by value (copy)
T
λ in C++
Syntax and
const auto l3 = [&]() { return y; }; // All by ref
Semantics
const auto l4 = [x]() { return x; }; // Only x by value (copy)
P
Closure Object
Lambdas vs. const auto lx = [=x](){ return x; }; // wrong syntax, no need for
Closures
N
FCO // = to copy x explicitly
Anatomy const auto l5 = [&y]() { return y; }; // Only y by ref
Parameters
Capture const auto l6 = [x, &y](){ return x * y; }; // x by value and y by ref
By Reference [&] const auto l7 = [=, &x](){ return x + y; }; // All by value except x
By Value [=]
Mutable // which is by ref
Restrictions const auto l8 = [&, y]() { return x - y; }; // All by ref except y which
Practice Examples
// is by value
Module Summary
const auto l9 = [this]() { } // capture this pointer
const auto la = [*this](){ } // capture a copy of *this
// since C++17
Programming in Modern C++ Partha Pratim Das M52.19
[&]()->rt{...}: Capture
Module M52
L
Partha Pratim
Das
int total_elements = 1;
E
Objectives & for_each(cardinal.begin(), cardinal.end(),
Outlines
[&](int i) { total_elements *= i; } ); // total_elements
T
λ in C++
Syntax and // can be changed
Semantics
P
Closure Object • Errors
Lambdas vs.
Closures [=](int i) { total_elements *= i; } );
N
FCO
Anatomy
Parameters error C3491: ’total_elements’: a by-value capture cannot be modified
Capture
By Reference[&]
in a non-mutable lambda
By Value [=]
Mutable
Restrictions
[](int i) { total_elements *= i; } );
Practice Examples
L
Partha Pratim
Das
int a; // now at global or namespace scope
E
Objectives & std::function<bool(int)> std::function<bool(int)>
Outlines returnClosure(int a) { // returns bool returnClosure() {
int b, c;
T
λ in C++ static int b, c; // now static ...
Syntax and
...
Semantics // won’t compile, but assume it would // now compiles
P
Closure Object return [](int x) return [](int x)
Lambdas vs.
Closures
{ return a*x*x + b*x + c == 0; }; { return a*x*x + b*x + c == 0; };
}
N
FCO }
Anatomy
Parameters // f is essentially a copy of // as before
Capture // lambda’s closure
By Reference[&] auto f = returnClosure(10); auto f = returnClosure();
By Value [=] ... ...
Mutable if (f(22)) // invoke the closure if (f(22)) // as before
Restrictions
Practice Examples • What are the values of a, b, c in the call? • a, b, c outlive returnClosure’s invocation
Module Summary
◦ returnClosure no longer active!
• Non-static locals referenceable only if • Variables of static storage duration always
captured referenceable
Programming in Modern C++ Partha Pratim Das M52.21
[&]()->rt{...}: Capture
L
Partha Pratim
Das void fill(std::vector<int>& v, T done) { int i = 0; while (!done()) { v.push_back(i++); } }
int main() {
E
Objectives & std::vector<int> stuff; // Fill the vector with 0, 1, 2, ... 7
Outlines
fill(stuff, [&]{ return stuff.size() >= 8; }); // [=] compiles but is infinite loop
T
λ in C++
Syntax and
for(auto it = stuff.begin(); it != stuff.end(); ++it) std::cout << *it << ' ';
Semantics std::cout << std::endl;
P
Closure Object
Lambdas vs.
Closures std::vector<int> myvec; // Fill the vector with 0, 1, 2, ... till the sum exceeds 10
N
FCO
fill(myvec, [&]{ int sum = 0; // [=] compiles but is infinite loop
Anatomy
Parameters
std::for_each(myvec.begin(), myvec.end(), [&](int i){ sum += i; });
Capture // [=] is error: assignment of read-only variable ‘sum’
By Reference [&] return sum >= 10;
By Value [=]
Mutable
}
Restrictions );
Practice Examples for(auto it = myvec.begin(); it != myvec.end(); ++it) std::cout << *it << ' ';
Module Summary std::cout << std::endl;
}
0 1 2 3 4 5 6 7
0 1 2 3 4
Programming in Modern C++ Partha Pratim Das M52.22
[=]()->rt{...}: Capture
Module M52
L
Partha Pratim
Das
std::vector<int> in, out(10);
E
Objectives &
Outlines for (int i = 0; i < 10; ++i)
T
λ in C++ in.push_back(i);
Syntax and
Semantics
P
Closure Object
Lambdas vs.
int my_mod = 3;
Closures
std::transform(in.begin(), in.end(), out.begin(),
N
FCO
Anatomy [=](int v) { return v % my_mod; });
Parameters
Capture
By Reference [&]
By Value [=]
for (auto it = out.begin(); it != out.end(); ++it)
Mutable std::cout << *it << ' ';
Restrictions
Practice Examples std::cout << std::endl;
Module Summary
0 1 2 0 1 2 0 1 2 0
Module M52
• Consider
L
Partha Pratim
Das int h = 10;
auto two_h = [=] () { h *= 2; return h; };
E
Objectives &
Outlines std::cout << "2h:" << two_h() << " h:" << h << std::endl;
T
λ in C++
Syntax and
Semantics error C3491: ’h’: a by-value capture cannot be modified in a non-mutable lambda
P
Closure Object
Lambdas vs. • λ closure objects have a public inline function call operator that:
Closures
N
FCO ◦ Matches the parameters of the lambda expression
Anatomy
Parameters ◦ Matches the return type of the lambda expression
Capture
By Reference [&]
◦ Is declared const
By Value [=] • Make mutable
Mutable
Restrictions int h = 10;
Practice Examples
auto two_h = [=] () mutable { h *= 2; return h; };
Module Summary
std::cout << "2h:" << two_h() << " h:" << h << std::endl;
2h:20 h:10
Programming in Modern C++ Partha Pratim Das M52.24
[=]()->rt{...}: Capture: Mutable
Module M52
int h = 10;
L
Partha Pratim
Das
auto f = [=] () mutable { h *= 2; return h; }; // h changes locally
E
Objectives & std::cout << "2h:" << f() << std::endl;
Outlines
std::cout << " h:" << h << std::endl;
T
λ in C++
Syntax and
Semantics
2h:20
P
Closure Object
Lambdas vs. h:10
Closures
N
FCO
Anatomy
Parameters
Capture
int h = 10;
By Reference[&] auto g = [&] () { h *= 2; return h; }; // h changes globally
By Value [=]
Mutable
std::cout << "2h:" << g() << std::endl;
Restrictions std::cout << " h:" << h << std::endl;
Practice Examples
Module Summary
2h:20
h:20
Module M52
int i = 1, j = 2, k = 3; // Global i, j, k
L
Partha Pratim
Das auto f = [i, &j, &k]() mutable
{
E
Objectives &
Outlines
auto m = [&i, j, &k]() mutable
T
λ in C++
Syntax and
{
Semantics
i = 4; // Local i of f
P
Closure Object
Lambdas vs.
Closures
j = 5; // Local j of m
N
FCO k = 6; // Global k
Anatomy
Parameters
};
Capture m();
By Reference [&]
By Value [=] std::cout << i << j << k; // Local i of f, Global j, Global k
Mutable
Restrictions
};
Practice Examples f();
Module Summary
std::cout << " : " << i << j << k; // Global i, j, k
426 : 126
Programming in Modern C++ Partha Pratim Das M52.26
[=]()->rt{...}: Capture: Mutable
Module M52
• Will this compile? If so, what is the result?
L
Partha Pratim
Das struct foo {
foo() : i(0) { }
E
Objectives &
Outlines void amazing(){ [=]{ i = 8; }(); } // i is captured by value
T
λ in C++ // Can it be changed without mutable?
Syntax and
Semantics int i;
P
Closure Object
};
Lambdas vs.
Closures foo f;
N
FCO
Anatomy
f.amazing();
Parameters std::cout << "f.i : " << f.i;
Capture
By Reference [&]
By Value [=] Output: f.i : 8
Mutable
Restrictions • this implicitly captured
Practice Examples
Module Summary • i actually is this->i which can be written from a member function as a data member.
So no mutable is required
L
Partha Pratim
Das
[i,j,&z](){...} // Okay
E
Objectives &
Outlines [&a,b](){...} // Okay
[z,&i,z](){...} // Bad, z listed twice
T
λ in C++
Syntax and
Semantics ◦ Default by value, explicit identifiers by reference
P
Closure Object
Lambdas vs.
[=,&j,&z](){...} // Okay
Closures
[=,this](){...} // Bad, no this with default =
N
FCO
Anatomy [=,&i,z](){...} // Bad, z by value
Parameters
Capture
◦ Default by reference, explicit identifiers by value
By Reference[&]
By Value [=]
[&,j,z](){...} // Okay
Mutable [&,this](){...} // Okay
Restrictions
Practice Examples
[&,i,&z](){...} // Bad, z by reference
Module Summary • Scope of Capture
◦ Captured entity must be defined or captured in the immediate enclosing lambda
expression or function
Programming in Modern C++ Partha Pratim Das M52.28
Closure Object: Capture: Mixed Examples
L
Partha Pratim
Das A& put(int v) { values.push_back(v); return *this; }
int extras() { int count = 0;
E
Objectives &
Outlines std::for_each(values.begin(), values.end(),
[=, &count](int v){ count += v % m_; });
T
λ in C++
Syntax and return count;
Semantics
}
P
Closure Object
Lambdas vs. };
Closures
A g(4);
N
FCO
Anatomy g.put(3).put(7).put(8);
Parameters std::cout << "extras: " << g.extras();
Capture
By Reference [&]
By Value [=]
extras: 6
Mutable
Restrictions
• Capture default by value
Practice Examples
• Capture count by reference, accumulate, return
Module Summary
• How do we get m ?
• Implicit capture of ’this’ by value
L
Partha Pratim { int j = 2; auto f = [=]{ std::cout << i / j; };
Das
f(); 4
}
E
Objectives &
Outlines auto f = [=](){ int j = 2; auto m = [=]{ std::cout << i / j; };
m(); };
T
λ in C++
f(); 4
Syntax and
Semantics
P
Closure Object auto f = [i](){ int j = 2; auto m = [=]{ std::cout << i / j; };
Lambdas vs. m(); };
Closures
f(); 4
N
FCO
Anatomy
Parameters
auto f = [](){ int j = 2; auto m = [=]{ std::cout << i / j; }; // Error C3493: ’i’ cannot be implicitly
Capture
m(); }; // captured because no default capture
By Reference [&] f(); // mode has been specified
By Value [=]
Mutable auto f = [=](){ int j = 2; auto m = [&]{ i /= j; }; m(); // Error C3491: ’i’: a by-value capture
Restrictions std::cout << "inner: " << i; }; // cannot be modified in a non-mutable
Practice Examples f(); std::cout << " outer: " << i; // lambda
Module Summary
auto f = [i]() mutable { int j = 2;
auto m = [&i, j]() mutable { i /= j; }; m();
std::cout << "inner: " << i; }; inner: 4
f(); std::cout << " outer: " << i; outer: 8
Programming in Modern C++ Partha Pratim Das M52.30
Module Summary
Module M52
L
Partha Pratim
Das
◦ Closure Objects
E
Objectives &
Outlines ◦ Parameters
◦ Capture
T
λ in C++
Syntax and
Semantics
P
Closure Object
Lambdas vs.
Closures
N
FCO
Anatomy
Parameters
Capture
By Reference [&]
By Value [=]
Mutable
Restrictions
Practice Examples
Module Summary