使用 [&] 捕获子句调用 lambda 时,在 C++ 中使用当前范围之外的变量时,是什么导致了奇怪的行为?

Wan*_*nze 10 c++ lambda gcc compilation

考虑以下代码:

#include <iostream>
#include <functional>

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

int main()
{
    f()();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在 Ubuntu 18.04 仿生 (WSL) 上使用 GCC 7.5.0 进行编译:

无优化

$ g++ -o main -std=c++14 -Wall main.cpp
$ ./main
666
Run Code Online (Sandbox Code Playgroud)

-O1

$ g++ -o main -O1 -std=c++14 -Wall main.cpp
$ ./main
0
Run Code Online (Sandbox Code Playgroud)

-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
Run Code Online (Sandbox Code Playgroud)

-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
Run Code Online (Sandbox Code Playgroud)

在 Windows 10 x64 上使用 TDM-GCC 9.2.0 进行编译:

无优化

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

>.\main.exe
666
Run Code Online (Sandbox Code Playgroud)

-O1

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

>.\main.exe
0
Run Code Online (Sandbox Code Playgroud)

-O2

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

>.\main.exe
0
Run Code Online (Sandbox Code Playgroud)

-O3

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

>.\main.exe
0
Run Code Online (Sandbox Code Playgroud)

在 Windows 10 x64 上使用 MSVC 19.27.29111 进行编译:

无优化

>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
Run Code Online (Sandbox Code Playgroud)

/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
Run Code Online (Sandbox Code Playgroud)

/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
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,不同的编译器和优化水平,节目输出6660或垃圾值。为什么会出现以上情况?

ALX*_*23z 11

x在 lambda 中按引用捕获,离开后f()它会随着x被破坏而成为悬空引用。你有一个经典的UB。为避免这种情况,您可以x通过写入[x][=]代替[&].