Search code examples
c++lambdareference

How does passing a lambda using std::ref in an algorithm work in C++?


I understand that when you pass a variable by reference, the function receives a reference to the original variable, allowing it to modify the variable's value directly. However, I'm curious about how this concept applies to callable objects like lambda functions? which part of the code gets modified? And does this apply to other callable objects, like functors?

Thank you.

#include <iostream>
#include <array>
int main()
{

    
    std::array<int, 5> data{ 1, 2, 3, 4, 5 };
    auto func = [sum = 0](int value) mutable -> int
    {
        sum += value;
        return sum;
    };

    std::for_each(data.begin(), data.end(), std::ref(func));
    std::cout << func(3) << '\n';

}

Solution

  • Lambdas are just shorthand for classes with an overloaded operator().

    That is, this:

    auto func = [sum = 0](int value) mutable -> int
    {
        sum += value;
        return sum;
    }
    

    is just shorthand for something like this:

    class AnonymousLambdaClass
    {
    private:
        int sum;
    
    public:
        AnonymousLambdaClass() : sum(0) {}
    
        auto operator()(int value) -> int
        {
            sum += value;
            return sum;
        }
    };
    auto func = AnonymousLambdaClass();
    

    Objects in the lambda's capture list are class members and the body of the lambda is an overloaded operator().


    std::ref is just a helper function that returns a std::reference_wrapper<T>. std::reference_wrapper is a pretty simple class template that looks something like this:

    template <typename T>
    class reference_wrapper
    {
    private:
        T* ref_;
    
    public:
        reference_wrapper(T& r) : ref_(&r) {}
    
        template <typename... Args>
        auto operator()(Args&&... args) const
        {
            return (*ref_)(std::forward<Args>(args)...);
        }
    
        T& get() const { return *ref_; }
        operator T&() const { return *ref_; }
    };
    

    Putting these together, std::ref(func) returns a std::reference_wrapper<AnonymousLambdaClass> that holds a pointer to func. When you call the resulting object you're calling the reference_wrapper's overloaded operator(), which in turn passes its arguments through to the operator() of the underlying AnonymousLambdaClass object which contains the body of the lambda that you wrote.