Lambda function is quite an intuitive concept of Modern C++ introduced in C++11, So there are already tons of articles on lambda function tutorial over the internet. But still, there are some untold things(like IIFE, types of lambda, etc.) left, which nobody talks about. Therefore, here I am to not only show you lambda function in C++ but we'll also cover how it works internally & other aspects of Lambda.
/!\: Originally published @ www.vishalchovatiya.com.
Title of this article is a bit misleading. Because lambda doesn't always synthesize to function pointer. It's an expression(precisely unique closure). But I have kept it that way for simplicity. So from now on, I will use lambda function & expression interchangeably.

What is lambda function?

A lambda function is short snippets of code that
In other words, it's just syntactic sugar. lambda function syntax is defined as:
[ capture list ] (parameters) -> return-type  
{   
    method definition
} 

Why we should use a lambda function?

struct print
{
    void operator()(int element)
    {
        cout << element << endl;
    }
};
int main(void)
{
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::for_each(v.begin(), v.end(), print());
    return 0;
}
std::for_each(v.begin(), v.end(), [](int element) { cout << element << endl; });

How lambda functions works internally?

[&i] ( ) { std::cout << i; }

// is equivalent to

struct anonymous
{
    int &m_i;
    anonymous(int &i) : m_i(i) {}
    inline auto operator()() const
    {
        std::cout << i;
    }
};

Benefits of using a lambda function

Learning lambda expression

Capture by reference/value
int main()
{
    int x = 100, y = 200;
    auto print = [&] { // Capturing object by reference
        std::cout << __PRETTY_FUNCTION__ << " : " << x << " , " << y << std::endl;
    };
    print();
    return 0;
}
Output:
main()::<lambda()> : 100 , 200
Lambda capture list
Passing lambda as parameter
template <typename Functor>
void f(Functor functor)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}
/* Or alternatively you can use this
void f(std::function<int(int)> functor)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
} 
*/
int g() { static int i = 0; return i++; }
int main()
{
    auto lambda_func = [i = 0]() mutable { return i++; };
    f(lambda_func); // Pass lambda
    f(g);           // Pass function
}
Output:
Function Type : void f(Functor) [with Functor = main()::<lambda(int)>]
Function Type : void f(Functor) [with Functor = int (*)(int)]
Capture member variable in lambda or this pointer
class Example
{
public:
    Example() : m_var(10) {}
    void func()
    {
        [=]() { std::cout << m_var << std::endl; }(); // IIFE
    }
private:
    int m_var;
};
int main()
{
    Example e;
    e.func();
}

C++ lambda function types

Generic lambda
const auto l = [](auto a, auto b, auto c) {};


// is equivalent to
struct anonymous
{
    template <class T0, class T1, class T2>
    auto operator()(T0 a, T1 b, T2 c) const
    {
    }
};
Variadic generic lambda
void print() {}
template <typename First, typename... Rest>
void print(const First &first, Rest &&... args)
{
    std::cout << first << std::endl;
    print(args...);
}
int main()
{
    auto variadic_generic_lambda = [](auto... param) {
        print(param...);
    };
    variadic_generic_lambda(1, "lol", 1.1);
}
mutable
lambda function
[]() mutable {}

// is equivalent to

struct anonymous
{
    auto operator()()  // call operator
    {
    }
};
Lambda as a function pointer
#include <iostream>
#include <type_traits>

int main()
{
    auto funcPtr = +[] {};
    static_assert(std::is_same<decltype(funcPtr), void (*)()>::value);
}
Higher-order returning lambda functions
const auto less_than = [](auto x) {
    return [x](auto y) {
        return y < x;
    };
};

int main(void)
{
    auto less_than_five = less_than(5);
    std::cout << less_than_five(3) << std::endl;
    std::cout << less_than_five(10) << std::endl;
    return 0;
}
constexpr
lambda expression
constexpr auto sum = [](const auto &a, const auto &b) { return a + b; };
/*
    is equivalent to
    constexpr struct anonymous
    {
        template <class T1, class T2>
        constexpr auto operator()(T1 a, T2 b) const
        {
            return a + b;
        }
    };
*/
constexpr int answer = sum(10, 10);

Closing words

I hope you enjoyed this article. I have tried to cover most of the intricacies around lambda with a couple of simple & small examples. You should use lambda wherever it strikes in your mind considering code expressiveness & easy maintainability like you can use it in custom deleters for smart pointers & with most of the STL algorithms.