{"id":1018,"date":"2015-02-18T23:43:07","date_gmt":"2015-02-19T07:43:07","guid":{"rendered":"http:\/\/www.elbeno.com\/blog\/?p=1018"},"modified":"2015-06-30T21:16:48","modified_gmt":"2015-07-01T04:16:48","slug":"a-problem-with-c-lambdas","status":"publish","type":"post","link":"https:\/\/www.elbeno.com\/blog\/?p=1018","title":{"rendered":"A problem with C++ lambdas?"},"content":{"rendered":"<p>C++ lambdas are wonderful for all sorts of reasons (especially with their C++14-and-beyond power). But I&#8217;ve run into a problem that I can&#8217;t think of a good way around yet.<\/p>\n<p>If you&#8217;re up to date with C++, of course you know that rvalue references and move semantics are a major thing. At this point, there are a lot of blog posts, youtube\/conference videos, and even books about how they work, and also about how forwarding references (formerly known as universal references) work with templates and <code>std::forward<\/code> to provide nice optimal handling of objects with move semantics.<\/p>\n<p>In C++14 the ability to handle lambda captures with move semantics was added with generalized lambda capture; another piece of the puzzle as far as move-semantic completeness goes.<\/p>\n<p>Another lesser-known but important piece of the complete picture is that class member functions can have reference qualifiers. (You can find this in the standard in section 9.3.1 [class.mfct.non-static]). This means that just as we write member functions with <code>const<\/code> modifiers, we can overload member functions with <code>&<\/code> or <code>&&<\/code> modifiers and they will be called according to the value category of <code>this<\/code>. So you know when you can call <code>std::move<\/code> on data members safely.<\/p>\n<p>Now, lambdas are <em>conceptually<\/em> like anonymous classes where the body is the <code>operator()<\/code> and the captured variables are the data members. And we can write lambdas with a <code>mutable<\/code> modifier indicating that the data members are mutable. (By far the common case is for them to be <code>const<\/code>, so the ordinary usage of <code>const<\/code> on a member function is inverted for lambdas.)<\/p>\n<p>I said <em>conceptually<\/em> because it turns out they aren&#8217;t completely like that in at least one important way: lambdas can&#8217;t have reference qualifiers. Maybe for good reason &#8211; how would the compiler implement that? How would the programmer who wanted that behaviour specify the overloads (s)he&#8217;s wanting? These are tricky questions to answer well. But it is a problem &#8211; as far as I can tell so far, there is no way to know, inside a lambda, what the value category of the lambda object is. So the performance promise of the move semantic model falls down in the face of safety concerns: I don&#8217;t know whether I can safely move from a captured variable inside a lambda.<\/p>\n<p>If anyone has any ideas about this, please let me know. Google and StackOverflow can tell me all about how move captures work with lambdas, but nothing about how to move things <em>out<\/em> of lambdas safely, or divine the value category of a lambda object. All the things I&#8217;ve tried have either not worked, or have resulted in suboptimalities of various kinds. (And frankly, if anything had worked, at this point I&#8217;d put it down to a compiler quirk and not to be relied on.)<\/p>\n<p>As far as I can tell, it&#8217;s a definite shortcoming of C++ that there&#8217;s no way to do this in a compile-time, type-inferred, lambda-land way. I don&#8217;t see this in the core language issues &#8211; is it a known problem, or is there a known way to solve it that I don&#8217;t know yet?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>C++ lambdas are wonderful for all sorts of reasons (especially with their C++14-and-beyond power). But I&#8217;ve run into a problem that I can&#8217;t think of a good way around yet. If you&#8217;re up to date with C++, of course you know that rvalue references and move semantics are a major&#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-1018","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\/1018","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=1018"}],"version-history":[{"count":4,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1018\/revisions"}],"predecessor-version":[{"id":1022,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1018\/revisions\/1022"}],"wp:attachment":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1018"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1018"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1018"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}