forked from peter-can-write/cpp-notes
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlambda-init-capture.html
More file actions
62 lines (62 loc) · 15 KB
/
lambda-init-capture.html
File metadata and controls
62 lines (62 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><meta name="exporter-version" content="Evernote Mac 6.3 (452849)"/><meta name="altitude" content="476.0238037109375"/><meta name="author" content="petergoldsborough@hotmail.com"/><meta name="created" content="2015-12-19 16:02:36 +0000"/><meta name="latitude" content="48.25678541103785"/><meta name="longitude" content="11.65505845421489"/><meta name="source" content="desktop.mac"/><meta name="updated" content="2015-12-21 19:47:05 +0000"/><title>lambda init capture</title></head><body>
<div>In C++11, it was not possible to move objects into lambdas, introducing possible expensive copies. In C++14, there exists the concept of “init captures”, also called “generalized lambda captures”, which allow you to specify, in the capture brackets:</div>
<div><br/></div>
<ul>
<li>the <b>name</b> of a data member in the closure class generated from the lambda</li>
<li>an <b>expression initializing</b> that data member</li>
</ul>
<div><br/></div>
<div>E.g. you might want to move a std::unique_ptr into a lambda capture:</div>
<div><br/></div>
<div><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">auto</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">pointer =</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #00a4e6">std</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">::</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #3d1d81">make_unique</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures"><</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">int</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">>(</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #f32300">5</span><span style="font-family: Menlo;"><span style="font-size: 11px;">);</span></span></div>
<div><span style="font-style: normal; font-variant: normal; font-weight: normal;"><span style="font-size: 11px;"><span style="font-family: Menlo;"><br/></span></span></span></div>
<div><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">auto</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">lambda = [p = std::move(pointer)] () {</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">return</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">*p +</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #f32300">5</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">; }; // note you can get rid of the empty parameter list ‘()'</span></div>
<div><br/></div>
<div>This will basically move the pointer object into the data member ‘p’ of the closure class created from the lambda. In C++11, this was not possible. One workaround is to emulate the lambda by an explicit class as would be created from it:</div>
<div><br/></div>
<div><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">class</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">Lambda<br/>
{<br/></span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">public</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">:<br/>
<br/></span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">using</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">T =</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #00a4e6">std</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">::</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #703daa">unique_ptr</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures"><</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">int</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">>;<br/>
<br/></span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">explicit</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">Lambda(</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #00a4e6">T</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">&& t_) : t(std::forward<T>(t_)) { }<br/>
<br/></span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">int</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">operator</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">()()</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">const</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">{</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">return</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">*t +</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #f32300">5</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">; }<br/>
<br/></span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">private</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">:<br/>
<br/></span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #00a4e6">T</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">t;<br/>
};<br/>
<br/></span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">int</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">main(</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">int</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">argc,</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">const</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">char</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">* argv[])<br/>
{<br/></span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">auto</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">pointer =</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #00a4e6">std</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">::</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #3d1d81">make_unique</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures"><</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">int</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">>(</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #f32300">5</span><span style="font-style: normal; font-variant: normal; font-weight: normal;"><span style="font-size: 11px;"><span style="font-family: Menlo;">);<br/>
<br/></span></span></span></div>
<div> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">auto</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures"> lambda =</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #4f8187">Lambda</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">(</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #00a4e6">std</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">::</span><span style="font-family: Menlo;"><span style="font-size: 11px;"><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #3d1d81">move</span><span style="font-style: normal; font-variant: normal; font-weight: normal;">(pointer));</span></span></span></div>
<div><span style="font-size: 11px;"><span style="font-family: Menlo;">}</span></span></div>
<div><br/></div>
<div>Another possibility is to emulate the move capture with a call to std::bind by</div>
<div><br/></div>
<ol>
<li>Moving the object to be captured into a function object produced by std::bind</li>
<li>Giving the lambda a reference to the “captured” object</li>
</ol>
<div><br/></div>
<div><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">auto</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">pointer = std::make_unique<</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">int</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">>(</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #f32300">5</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">);<br/>
<br/></span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">auto</span> <span style="font-style: normal; font-variant: normal; font-weight: normal; line-height: normal;"><span style="font-size: 11px;"><span style="font-family: Menlo;">lambda = std::bind(</span></span></span></div>
<div><span style="font-size: 11px;"><span style="font-family: Menlo;"> [] (<span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">const</span></span></span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">std::unique_ptr<</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">int</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">>& p) {</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">return</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">*p +</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #f32300">5</span><span style="font-size: 11px;"><span style="font-family: Menlo;">; },</span></span></div>
<div><span style="font-style: normal; font-variant: normal; font-weight: normal;"><span style="font-size: 11px;"><span style="font-family: Menlo;"> std::move(pointer)</span></span></span></div>
<div><span style="font-size: 11px;"><span style="font-family: Menlo;">);</span></span></div>
<div><br/></div>
<div>Thus, in conclusion:</div>
<div><br/></div>
<ul>
<li>It’s not possible to move-construct an object into a C++11 closure, but it’s possible to move-construct an object into a C++11 bind object.</li>
<li>Emulating move-capture in C++11 consists of move-constructing an object into a bind object, then passing the move-constructed object to the lambda by reference.</li>
<li>Because the lifetime of the bind object is the same as that of the closure, it’s possible to treat objects in the bind object as if they were in the closure.</li>
</ul>
<div><br/></div>
<div>Note about mutability: By default, the call operator of a lambda is const, thus any captured by-value data-members are non-modifiable. That is why in the call to bind, we made the unique_ptr parameter const& (to emulate that it would have been const if we passed it in the init-capture). If we wanted to emulate the unique-ptr being modifiable, we should have written:</div>
<div><br/></div>
<div><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">auto</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">lambda =</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #00a4e6">std</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">::</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #3d1d81">bind</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">(<br/>
[] (</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #848784">/* non-const */</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #00a4e6">std</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">::</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #703daa">unique_ptr</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures"><</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">int</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">>& p)</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">mutable</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">{</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #e448ab">return</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">*p +</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #f32300">5</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">; },<br/>
</span> <span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #00a4e6">std</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">::</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #3d1d81">move</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures">(</span><span style="font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #3d1d81">pointer</span><span style="font-style: normal; font-variant: normal; font-weight: normal; line-height: normal;"><span style="font-size: 11px;"><span style="font-family: Menlo;">)</span></span></span></div>
<div><span style="font-size: 11px;"><span style="font-family: Menlo;">);</span></span></div>
<div><br/></div>
<div><br/></div>
</body></html>