首页 > 解决方案 > 当调用带有 [&] 捕获子句的 lambda 时,是什么导致了奇怪的行为,其中在 C++ 中使用了当前范围之外的变量?

问题描述

考虑以下代码:

#include <iostream>
#include <functional>

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

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

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

无优化

$ 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

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

无优化

>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

在 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

/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

如您所见,使用不同的编译器和优化级别,程序会输出6660或垃圾值。为什么会出现上述情况?

标签: c++gcclambdacompilation

解决方案


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


推荐阅读