{{announcement.body}}
{{announcement.title}}

5 Curious C++ Lambda Examples: Recursion, constexpr, Containers, and More

DZone 's Guide to

5 Curious C++ Lambda Examples: Recursion, constexpr, Containers, and More

In this post, see a few interesting lambda examples and learn how to write a recursive lambda, store them in a container, and invoke at compile time.

· IoT Zone ·
Free Resource

Please have a look at my quick blog post where I'll show you a few interesting lambda examples. Do you know how to write a recursive lambda? Store them in a container? Or invoke at compile time?

See how in this article.

1. Recursive Lambda With std::function

Writing a recursive function is relatively straightforward: inside a function definition, you can call the same function by its name. How about lambdas?

C++
 




xxxxxxxxxx
1


 
1
int main() {
2
    auto factorial = [](int n) {
3
        return n > 1 ? n * factorial(n - 1) : 1;
4
    };
5
    return factorial(5);
6
}



This, unfortunately, doesn't compile...

How can we fix this?

One way is to use std::function:

C++
 




xxxxxxxxxx
1


 
1
#include <functional>
2
 
           
3
int main() {
4
    const std::function<int(int)> factorial = [&factorial](int n) {
5
        return n > 1 ? n * factorial(n - 1) : 1;
6
    };
7
    return factorial(5);
8
}



This time, we need to capture factorial and then we can refer to it inside the lambda body.

And since C++14 we can also leverage generic lambdas and write the following code:

C++
 




xxxxxxxxxx
1


 
1
int main() {
2
    const auto factorial = [](int n) {
3
        const auto fact_impl = [](int n, const auto& impl) -> int {
4
            return n > 1 ? n * impl(n - 1, impl) : 1;
5
        };
6
        return fact_impl(n, fact_impl);
7
    };
8
    return factorial(5);
9
}



This time, it's even more complicated (but doesn't require heavy use of std::function). It uses internal lambda for the main computation and then it's passed as a generic argument.

But I wonder: have you ever used recursive lambdas? Or, it's better to rely on recursive functions (which seems to be far more comfortable to use and write).

2. constexpr Lambdas

But that's not all with recursion... :)

Since C++17 we can write lambdas that have the call operator defined as constexpr. We can use this property and expand the recursive example into:

C++
 




xxxxxxxxxx
1


 
1
int main() {
2
    constexpr auto factorial = [](int n) {
3
        constexpr auto fact_impl = [](int n, const auto& impl) -> int {
4
            return n > 1 ? n * impl(n - 1, impl) : 1;
5
        };
6
        return fact_impl(n, fact_impl);
7
    };
8
    static_assert(factorial(5) == 120);
9
}



And in C++20, you can even apply consteval to mark lambdas which can be evaluated only at compile time.

3. Storing Lambdas in a Container

This might be a bit cheating... but we can theoretically store lambdas in a container.

While closure types have default constructors deleted (unless it's stateless lambda in C++20), we can do a little hack and store all lambdas as std::function objects. For example:

C++
 




xxxxxxxxxx
1
28


 
1
#include <functional>
2
#include <iostream>
3
#include <vector>
4
 
           
5
int main() {
6
    std::vector<std::function<std::string(const std::string&)>> vecFilters;
7
 
           
8
    vecFilters.emplace_back([](const std::string& x) { 
9
        return x + " Amazing"; 
10
    });
11
    vecFilters.emplace_back([](const std::string& x) { 
12
        return x + " Modern"; 
13
    });
14
    vecFilters.emplace_back([](const std::string& x) { 
15
        return x + " C++"; 
16
    });
17
    vecFilters.emplace_back([](const std::string& x) { 
18
        return x + " World!"; 
19
    });
20
 
           
21
    const std::string str = "Hello";
22
    auto temp = str;
23
 
           
24
    for (auto &entryFunc : vecFilters)  
25
        temp = entryFunc(temp);
26
 
           
27
    std::cout << temp;
28
}




4.Generic lambdas and Help With Deduction

C++14 brought an important addition to lambdas: generic lambda arguments. Here's one example that shows why is it useful:

C++
 




xxxxxxxxxx
1
16


 
1
#include <algorithm>
2
#include <iostream>
3
#include <map>
4
#include <string>
5
 
           
6
int main() {
7
    const std::map<std::string, int> numbers { 
8
        { "one", 1 }, {"two", 2 }, { "three", 3 }
9
    };
10
 
           
11
    std::for_each(std::begin(numbers), std::end(numbers), 
12
         [](const std::pair<std::string, int>& entry) {
13
             std::cout << entry.first << " = " << entry.second << '\n';
14
         }
15
    );
16
}



Do you know what's the mistake here? Is the argument type appropriately specified in the inner lambda for for_each?

I specified: const std::pair<std::string, int>& entry.

But it's wrong as the type of the key/value pair inside a map is:

That's why the compiler has to create unwanted temporary copies and then pass them to my lambda.

We can quickly fix this by using a generic lambda from C++14.

C++
 




xxxxxxxxxx
1


 
1
std::for_each(std::begin(numbers), std::end(numbers), 
2
    [](const auto& entry) {
3
        std::cout << entry.first << " = " << entry.second << '\n';
4
    }
5
);



Now the types match, and no additional copies are created.

5. Returning a Lambda

If you want to return a lambda from a function (for example for partial function application, currying), then it's not straightforward because you don't know the exact type of the closure object.

In C++11 one way was to use std::function:

C++
 




xxxxxxxxxx
1
10


 
1
#include <functional>
2
 
           
3
std::function<int(int)> CreateLambda(int y) {
4
    return [y](int x) { return x + y; };
5
}
6
 
           
7
int main() {
8
    auto lam = CreateLambda(10);
9
    return lam(32);
10
}



But since C++14, we can leverage the auto type deduction for return types and just write:

C++
 




xxxxxxxxxx
1


 
1
auto CreateLambda(int y) {
2
    return [y](int x) { return x + y; };
3
}
4
 
           
5
int main() {
6
    auto lam = CreateLambda(10);
7
    return lam(32);
8
}



The above code is far simpler and cheaper as we don't need to use std::function.

Summary

In this quick article, I showed you five interesting lambda examples. They might not be common, but shows flexibility and sometimes even complexity of the closure types.

Do you use lambdas in such contexts?
Or maybe you have even more complicated examples?
Share your experience in comments below the article.

If You Want to Know More

Most of the examples from this article comes from my book: "C++ Lambda Story" available @Leanpub and also @Amazon.

Topics:
c++, glsl, graphics, iot, opengl, shaders, tutorial, vc++, windows

Published at DZone with permission of Bartłomiej Filipek , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}