Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What causes the strange behaviors when calling a lambda with [&] capture clause inside which a variable outside current scope is used in C++?

Consider the following code:

#include <iostream>
#include <functional>

std::function<void ()> f()
{
    int x = 666;
    return [&] { std::cout << x << std::endl; };
}

int main()
{
    f()();
    return 0;
}

Compiling with GCC 7.5.0 on Ubuntu 18.04 bionic (WSL):

No optimization

$ g++ -o main -std=c++14 -Wall main.cpp
$ ./main
666

-O1

$ g++ -o main -O1 -std=c++14 -Wall main.cpp
$ ./main
0

-O2

$ g++ -o main -O2 -std=c++14 -Wall main.cpp
main.cpp: In function ‘int main()’:
main.cpp:7:31: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
     return [&] { std::cout << x << std::endl; };
                               ^
$ ./main
32699

-O3

$ g++ -o main -O3 -std=c++14 -Wall main.cpp
main.cpp: In function ‘int main()’:
main.cpp:7:31: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
     return [&] { std::cout << x << std::endl; };
                               ^
$ ./main
32528

Compiling with TDM-GCC 9.2.0 on Windows 10 x64:

No optimization

>g++ -o main.exe -std=c++14 -Wall main.cpp

>.\main.exe
666

-O1

>g++ -o main.exe -O1 -std=c++14 -Wall main.cpp

>.\main.exe
0

-O2

>g++ -o main.exe -O2 -std=c++14 -Wall main.cpp

>.\main.exe
0

-O3

>g++ -o main.exe -O3 -std=c++14 -Wall main.cpp

>.\main.exe
0

Compiling with MSVC 19.27.29111 on Windows 10 x64:

No optimization

>cl /EHsc main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29111 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp
Microsoft (R) Incremental Linker Version 14.27.29111.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:main.exe
main.obj

>.\main.exe
8402693

/O1

>cl /EHsc /O1 main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29111 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp
Microsoft (R) Incremental Linker Version 14.27.29111.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:main.exe
main.obj

>.\main.exe
666

/O2

>cl /EHsc /O2 main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29111 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp
Microsoft (R) Incremental Linker Version 14.27.29111.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:main.exe
main.obj

>.\main.exe
666

As you can see, with different compilers and optimization levels, the program outputs 666, 0, or a garbage value. Why does the above happen?

like image 823
wtz Avatar asked Sep 26 '20 18:09

wtz


People also ask

Is it bad to call a lambda function across service boundaries?

In which case it’s not a problem to call a Lambda function directly across service boundaries. And what you lose in flexibility you gain in performance and cost efficiency as you cut out a layer from the execution path. There are also other exceptions to keep in mind.

What is the impact of 2nd Lambda invocation on latency?

This impacts the end-to-end latency. The 2nd Lambda invocation carries an extra cost for the invocation request. Since Lambda durations are paid in 100ms blocks, so you will pay for the amount of “roll-up” time for both caller and callee functions. You will pay for the idle wait time while the caller function waits for a response from the callee.

Can I call a lambda function from another lambda function?

If you utter the words “I call a Lambda function from another Lambda function” you might receive a bunch of raised eyebrows. It’s generally frowned up for some good reasons, but as with most things, there are nuances to this discussion. In most cases, a Lambda function is an implementation detail and shouldn’t be exposed as the system’s API.

Why are lambdas paid in 100ms blocks?

Since Lambda durations are paid in 100ms blocks, so you will pay for the amount of “roll-up” time for both caller and callee functions. You will pay for the idle wait time while the caller function waits for a response from the callee.


1 Answers

You capture x by reference in the lambda and after leaving f() it becomes a dangling reference as x gets destroyed. You have a classic UB. To avoid it you can capture x by value by writing [x] or [=] instead of [&].

like image 51
ALX23z Avatar answered Oct 21 '22 14:10

ALX23z