{"id":1068,"date":"2015-03-16T13:48:52","date_gmt":"2015-03-16T20:48:52","guid":{"rendered":"http:\/\/www.elbeno.com\/blog\/?p=1068"},"modified":"2015-06-30T21:16:14","modified_gmt":"2015-07-01T04:16:14","slug":"another-myth-about-c-lambdas","status":"publish","type":"post","link":"https:\/\/www.elbeno.com\/blog\/?p=1068","title":{"rendered":"Another myth, about C++ lambdas"},"content":{"rendered":"<p>Myth: <em>Lambda expressions can cause heap allocations.<\/em><\/p>\n<p>I see this myth coming up a lot. People think that lambdas can cause a heap allocation &#8211; on Reddit, on StackOverflow, on Channel9 comments, in personal conversations. I don&#8217;t blame people for thinking this &#8211; C++ is complex. But it always seems to be some vague notion of &#8220;heap allocations can happen under some circumstances&#8221;. So let&#8217;s think about it more clearly.<\/p>\n<p>Lambdas are a language feature. The compiler will create a closure object representing the function literal you write. The closure object is a value type, created as a prvalue and with automatic storage when assigned to <code>auto<\/code> (as you must, because its type is unutterable). The only way heap allocation can occur is if you by-value capture a variable whose copy constructor does heap allocation (and this isn&#8217;t a consequence of the lambda per se, as I hope you can see).<\/p>\n<p>You can use lambdas all day long on the stack, assigned to <code>auto<\/code> variables, and never incur a heap allocation. Since lambdas are a language feature, it&#8217;s unclear if they <em>could<\/em> do allocation even if it were desirable &#8211; in C++, allocation is in the realm of library code, and I think it would almost certainly entail semantic difficulties for the language, as well as difficulties with exceptions and the like.<\/p>\n<p>Where the confusion comes from, perhaps, is a conflation of lambdas with <code>std::function<\/code>. (Because lambdas are commonly assigned to <code>std::function<\/code> for storage and passing around?) <code>std::function<\/code> is a library construct that wraps a callable using type erasure, and may very well incur heap allocation. In order to wrap any callable, it has to create an internal class in the standard type-erasure way, and that might involve an allocation.<\/p>\n<p>However, <code>std::function<\/code> does have a couple of common optimizations up its sleeve. First is the so-called &#8220;small functor optimization&#8221; &#8211; a buffer inside a <code>std::function<\/code> object that is typically big enough to store a few pointers and can be used to store the internal object, assuming it will fit. This allows <code>std::function<\/code> to avoid heap allocation in common cases (typically just one or maybe two captures).<\/p>\n<p>The second optimization is a space optimization. The typical type-erasure pattern involves calling a virtual function on an internal base class, whose derived class is parametrized on the actual type passed in. But every lambda has a different type, so a naive implementation of this could result in many vtables being generated. So <code>std::function<\/code> commonly optimizes the call machinery, basically by supplanting the normal C++ virtual call process with a free function implementation that doesn&#8217;t cause vtable bloat.<\/p>\n<p>And that&#8217;s about it. Lambdas don&#8217;t (intrinsically) cause any heap allocation. Ever. When you assign to a <code>std::function<\/code>, that may cause an allocation, but for a small number of captures, the small functor optimization will probably apply.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Myth: Lambda expressions can cause heap allocations. I see this myth coming up a lot. People think that lambdas can cause a heap allocation &#8211; on Reddit, on StackOverflow, on Channel9 comments, in personal conversations. I don&#8217;t blame people for thinking this &#8211; C++ is complex. But it always seems&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,8],"tags":[],"class_list":["post-1068","post","type-post","status-publish","format-standard","hentry","category-cpp","category-programming"],"_links":{"self":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1068","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1068"}],"version-history":[{"count":4,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1068\/revisions"}],"predecessor-version":[{"id":1072,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1068\/revisions\/1072"}],"wp:attachment":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1068"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1068"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1068"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}