• entries
146
435
• views
198845

# The New C++ - lambdas

3730 views

Ah lambdas. If you've used any functional languages, python, ruby, or C# (or many other languages), you are probably familiar with the concept of lambdas. However, if you've been doing C++ for a while and haven't used boost's lambda library then let me be the first to introduce you to the concept... A lambda is, essentially, an unnamed (or anonymous) function. You can use lambdas as a simplification for many common scenarios that crop up frequently throughout code. Examples include a sort predicate, a complex search predicate, or an event system.

# Lambda Basics

Probably the most common type of lambda you'll encounter is one that simply takes a set of parameters and returns a result based on them. One of the simplest examples of this is the factorial function. The factorial function returns the product of all numbers from 1 to N, and is typically represented as N! (reads as "N factorial". It grows extremely quickly, and will typically outpace the size of an integer in a few steps. If we take the code from our previous entries and use that as a starting point, we can use the v1 vector of integers and compute the factorial of 10:
 auto factorial = [](int lhs, int rhs) { return lhs * rhs; }; auto result = std::accumulate(v1.begin(), v1.end(), 1, factorial); std::cout<The lambda portion of the previous statements is clearly the definition of the factorial variable. The lambda syntax in C++ leaves much to be desired, but I shall endeavor to explain the syntax...We can break it up into four basic parts, the capture statement, the parameters, the return type, and the body of the lambda: [ capture-statement ] ( parameters ) /* optional if empty */ -> return-type /*optional*/ { body }Capture statements and return-type need some explaining, the parameters and body though are pretty self-explanatory. The only thing I will state about the parameters argument is: if you have no arguments then the parenthesis are optional. I tend to insert them anyways, just for clarity.The Capture StatementThe capture statement is used to capture variables that are not local to the body of the lambda. You can capture variables either by value or by reference, and you can also capture all variables available in a scope by value or by reference. Again, the syntax is a bit odd: auto push_back_c_v1_byref = [&v1](int i) { v1.push_back(i); }; auto push_back_c_all_byref = [&](int i) { v1.push_back(i); }; auto print_c_v1_byval = [v1]() { for each(auto x in v1) { std::cout< auto print_c_all_byval = [=]() { for each(auto x in v1) { std::cout< auto print_c_all_byval_v2_byref = [=,&v2]() { for each(auto x in v1) { std::cout< push_back_c_v1_byref(11); push_back_c_all_byref(11); print_c_v1_byval(); print_c_all_byval();The first two capture statements are reference captures. The first one, push_back_c_v1_byref, only captures the v1 vector by reference and nothing else is captured. Since we're capturing v1 by reference we can manipulate v1 in various ways, such as (in this case) inserting an item into the vector. The second capture statement is known as a "default capture by reference" and captures everything in scope by reference. Thus we're able to manipulate v1 without having to explicitly capture it. Obviously there is an efficiency concern here, however compilers should be able to optimize out any references to objects NOT used by the lambda.The second two captures show by-value capturing. With the first one, print_c_v1_byval, taking a copy of v1. This does result in a copy constructor invocation. As such, for something like this print method, it's not necessarily terribly efficient. Although, for value types or for types you want to ensure the lambda doesn't modify, taking it by-value can be an advantage. The second capture uses the "default capture by value", and much like the "default capture by reference", the compiler will likely optimize out anything you don't explicitly use. The for-each syntax we'll get into later, but it's a Visual Studio 2010 (and VS11) extension to the language. The C++11 version is for(T v : container).The last capture statement is an interesting one. We declare the default capture to be by-value, but then we explicitly state that we wish to capture v2 by reference. Note that this is also possible to be done vice-versa. You can capture everything by reference except those items you explicitly designate to be by-value.The Return TypeThe return type of a lambda is the one optional component of the lambda declaration (as you may have noticed in the examples above, I've omitted the return type for all of them). The return type is only mandatory when the type cannot be inferred from the body of the lambda, or when the return type does not match what you are attempting to return. Here's a simple example: auto print_hello_cstr = []() { return "hello"; }; auto print_hello_stdstr = []() -> std::string { return "hello"; }; std::cout< std::cout<Now, clearly the first one returns a char const*, as that's the C++ string literal type. The second one, however, returns a string type, invoking the appropriate constructor of the string type. There are other cases though where you might find that you have to explicitly specify the result type, auto return_something = [](int a) { if(a > 40) return 4.5f; return 3; };This lambda poses a bit of a problem, there are actually two problems here. The first is that the return type is not clear (it could be float or int), and the conditional does nothing to clarify it. Moreover, simply changing the literal integer to a literal float doesn't solve all of the issues, and so the standard requires you to specify the expected return type if the lambda is more than just a simple return statement. We can fix this in one of two ways, in the first way we reduce the statement to a simple return: auto return_something = [](int a) { return (a > 40) ? 4.5f : 3; };While in the second method we simply indicate the expected return type: auto return_something = [](int a) -> float { if(a > 40) return 4.5f; return 3; };Applying Our KnowledgeWe've gone through all of this effort to reach this point, going back to our original piece of code, which we've updated with auto and decltype, we have end up with the resulting piece of code:#include #include #include templateauto find_first_pair(Sequence1 const& seq1, Sequence2 const& seq2, MatchBinaryFunctor match) -> decltype(std::make_pair(seq1.end(), seq2.end())){ for(auto itor1 = seq1.begin(); itor1 != seq1.end(); ++itor1) { for(auto itor2 = seq2.begin(); itor2 != seq2.end(); ++itor2) { if(match(*itor1, *itor2)) { return std::make_pair(itor1, itor2); } } } return std::make_pair(seq1.end(), seq2.end());}bool is_equal_and_odd(int lhs, int rhs) { return lhs == rhs && lhs % 2 != 0;}int main() { int v1ints[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int v2ints[] = {13, 4, 5, 7}; std::vector v1(v1ints, v1ints + 10); std::vector v2(v2ints, v2ints + 4); auto p = find_first_pair(v1, v2, is_equal_and_odd); std::cout<<*p.first<<":"<<*p.second<}The only real outstanding issue, at the moment, is the function is_equal_and_odd. This function is exactly the kind of thing lambdas were designed to help eliminate. Thus if we apply our knowledge of lambdas we can come up with a quick replacement to eliminate that entire function, inlining it into the function that's doing the work:int main() { int v1ints[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int v2ints[] = {13, 4, 5, 7}; std::vector v1(v1ints, v1ints + 10); std::vector v2(v2ints, v2ints + 4); auto p = find_first_pair(v1, v2, [](int lhs, int rhs) { return lhs == rhs && lhs % 2 != 0; }); std::cout<<*p.first<<":"<<*p.second<}Which is, in my opinion, quite a bit cleaner and simpler than having a separate function definition hanging around. 
 3 Sign in to follow this   Followers 0 
 Previous entry The New C++, Part 2 - decltype Next entry The New C++ - functions 1 Comment Recommended Comments mrbastard 1577 Posted March 11, 2012 Great post. For me, Lambdas make coding in c++ exciting again. The only thing I can compare it to is the fun of using lambdas / list comprehensions in Python - the code just flows from your fingertips as fast as you can think. Being able to use std algortihms without explicitly defining a functor makes a huge difference - even if I decide later that I'm going to use the lambda's logic in a few different scopes and it's worth redefining it as an explicit functor, the convenience of being able to write the initial logic without breaking flow to go away and write boilerplate is a huge productivity win. I found lambda syntax a bit odd at first too, but I really hope it becomes a mainstream part of the average c++ programmer's mental toolset. For all it's terseness, I think in time it will actually improve readability of c++ codebases. When trying to understand a bit of code, it's sometimes much easier to read some logic in place (in a lambda) than to have to go and read the header for a simple adapter or functor. One of the coolest uses of lambdas (I think) is to use them in place of 'bind' to combine functors or to do a sort of polymorphism of parameterisation. I've no idea what to call this, but essentially most situations where you want to have a collection of arbitrary functions to call on arbitrary data you can use lambdas to bundle up the logic and data into an object whose operator() that takes the appropriate minimal number and type of parameters for the collection. Unfortunately, lambda types being unrelated means you either have to use std::function or a template parameter to talk about your 'minimal' abstract functor interface. Maybe we'll get polymorphic lambdas (i.e. of related types) at some point. Still, a great way to write little adapters without needing lots of boilerplate. 0 Share this comment Link to comment Create an account or sign in to comment You need to be a member in order to leave a comment Create an account Sign up for a new account in our community. It's easy! Register a new account Sign in Already have an account? Sign in here. Sign In Now Advertisement 
 Advertisement Blog Entries OOP is dead, long live OOP By Hodgman in 22 Racing Series    55 Diablo:Immortal By JTippetts in /* Why you crying? */    46 Survive, Loot, and Discover! It's Time for the Dungeon Crawler Challenge By khawk in Challenges    15 Bumpy World By Gnollrunner in Zemli Drakona MMO Game Development    14 Navigation Mesh : Wall Collision Avoidance By thecheeselover in 3D, AI, procedural generation and black jack    12 GameDev.net GameDev.net Articles GameDev.net Event Coverage GameDev.net Forums GameDev.net Blogs GameDev.net Gallery GameDev.net News GameDev.net Projects GDNet Chat All Activity Search In Everywhere This Blog This Entry More options... Find results that contain... All of my search term words Any of my search term words Find results in... Content titles and body Content titles only Home Blogs There is no escape from the Washu The New C++ - lambdas 
 
 
 × Existing user? Sign In Sign Up Browse Back Articles & Tutorials Back All Categories Audio Business Game Design Industry Programming Visual Arts Columns Back GameDev Unboxed Event Coverage Back All Events Game Developers Conference Power Up Digital Games Conference GameDev.Market Links News Podcasts Back All Podcasts Game Dev Loadout Archive Community Back Beginners Back Beginners Group Beginners Forum Beginners Resources Blogs Calendar Chat Forums Back All Forums Audio Business Game Design Programming Visual Arts Community GameDev Challenges Affiliates Topical Workshops Gallery Groups Back For Beginners GameDev Challenges All Groups Projects Back All Projects Games Game Assets Game Mods Developer Tools Store Forums Back All Forums For Beginners Audio Back Music and Sound FX Games Career Development Business Back Games Career Development Production and Management Games Business and Law Game Design Back Game Design and Theory Writing for Games Programming Back Artificial Intelligence Engines and Middleware General and Gameplay Programming Graphics and GPU Programming Math and Physics Networking and Multiplayer Visual Arts Back 2D and 3D Art Critique and Feedback Community Back GameDev Challenges GDNet Lounge GDNet Comments, Suggestions, and Ideas Coding Horrors Your Announcements Hobby Project Classifieds Indie Showcase Affiliates Back NeHe Productions AngelCode Topical Workshops Careers Back Contractors Hobby Projects Game Jobs Back Browse on GameDev.Jobs Post a Job Members Back Subscriptions Chat Guidelines Leaderboard Online Users Awards Search Back All Activity My Activity Streams Back Latest Topics Featured Blogs Search Important Information By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.   I accept GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry. Sign me up!