When does C++ lambda function captures the variables
Asked Answered
M

1

1

The following code block launches a thread via a lambda expression. This lambda expression captures a local variable "testVar" by reference. By the time the thread function executes that local variable goes out of scope and the thread function will not be able to access the correction values. Briefly tested capturing by copy by changing "&" to "=" in the capture clause. The for loop seems to print the correct values. My question is when does the capture happen? Does it happen when the lambda function is created or when the thread gets executed. In the latter case, capturing by copy should not produce the right results.

#include <iostream>
#include <string>
#include <vector>
#include <thread>

int main()
{
  std::thread myThread;
  {
      std::vector<int> testVar(10, 128);
      std::cout<<"Trying to create thread"<<std::endl;
      myThread = std::thread([&](){std::this_thread::sleep_for(std::chrono::seconds(3));for(int i=0; i<10; i++){std::cout<<testVar[i]<<std::endl;}});
  }
  myThread.join();
}

Capturing by reference produces the following result

Trying to create thread 1580344384 21851 1580269584 21851 128 128 128 128 128 128

Capturing by copy produces the following result

Trying to create thread 128 128 128 128 128 128 128 128 128 128

Middy answered 29/11, 2022 at 3:21 Comment(3)
not exactly a dupe but see: https://mcmap.net/q/612380/-passing-parameters-to-lambda-in-cCounterweight
Your "evidence" is suspect, because undefined behavior sometimes "seems to" produce the "correct" values. See the hotel story.Chemmy
I failed to find a duplicate for the asked question "when does the capture happen?" Maybe someone else can find one. In the meantime, I could suggest an experiment. Try auto lambda = [=](){for(auto x : testVar) std::cout<<x<<'\n';}; testVar = std::vector<int>(6, 32); lambda();. What is the result? What does that tell you about when the capture is made?Chemmy
C
1

A lambda is an expression whose value effectively is a functional object. It captures variables when the expression is evaluated, and that happens at the same time as any other expression would be evaluated if you put it in the same place. That is to say, it would happen in program order.

If we expand your code a bit,...

std::vector<int> testVar(10, 128);
std::cout<<"Creating the thread function";
auto thread_function = [&](){
    std::this_thread::sleep_for(std::chrono::seconds(3));
    for(int i=0; i<10; i++){
        std::cout<<testVar[i]<<std::endl;
    }
};
std::cout<<"Trying to create thread"<<std::endl;
myThread = std::thread(thread_function);

...Then the lambda would be evaluated, and the testVar reference would be captured, after the program printed "Creating the thread function," and before it printed "Trying to create thread." Clearly, that all happens before the thread object is created.

Cori answered 29/11, 2022 at 21:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.