Search code examples
c++lambdacapturenamed

C++ Will a lambda shallow copy const Type& if it is given a named capture like [copy=val](){}?


I'm doing some work that requires multiple frames to process and I want to know if the named copy-by-value syntax for lambdas will copy the data from a const ref, or if it will create a new refernce to that data. In my case the data is on the stack and I need the lambda to hold on to a copy of the data.

consider the very redacted code sample

#include<functional>
#include<iostream>

struct FData
{
    int x = 5;
};

std::function<void()> test(const FData& in)
{
    return [copy = in]()
    {
        std::cout << copy.x << std::endl;
    };
}

int main()
{
    std::function<void()> callback;

    {//scoped to destroy obj1
        FData obj1;
        callback = test(obj1);
        obj1.x = 3;
    }

    {
        FData obj2;
        obj2.x = 7; //attempt to overwrite obj1 memory

        callback(); //use ?copy? of obj1 in the callback
    }

    return 0;
}

This outputs 5, so it seems to be correctly copying the data. But I have not been able to safely verify the type by means like typeid().name(). I'm not sure of a way to know this other than testing.


Solution

  • Capture by value works like any other assignment by value. In your example, the lambda's copy member will be a copy of the FData that in refers to, copy will not be a reference to that FData. It doesn't matter that in is itself a reference, because copy is not a reference.

    You can easily prove this without resorting to RTTI:

    #include <functional>
    #include <iostream>
    
    struct FData
    {
        int id = 1;
        int x = 5;
    };
    
    std::function<void()> test(const FData& in)
    {
        return [copy = in]()
        {
            std::cout << "addr=" << &copy << " id=" << copy.id << " x=" << copy.x << std::endl;
        };
    }
    
    int main()
    {
        std::function<void()> callback;
    
        {//scoped to destroy obj1
            FData obj1;
    
            std::cout << "addr=" << &obj1 << " id=" << obj1.id << " x=" << obj1.x << std::endl;
    
            callback = test(obj1);
        }
    
        {
            FData obj2;
            obj2.id = 2;
            obj2.x = 7; //attempt to overwrite obj1 memory
    
            std::cout << "addr=" << &obj2 << " id=" << obj2.id << " x=" << obj2.x << std::endl;
    
            callback();
        }
    
        return 0;
    }
    

    Live Demo

    Output:

    addr=0x7ffe23f86bb8 id=1 x=5
    addr=0x7ffe23f86bb8 id=2 x=7
    addr=0x7ffe23f86bc0 id=1 x=5