为何C++ Lambda会导致析构函数被额外调用?
为什么不带&的auto参数无法匹配std::function期望的引用类型?
测试程序
#include <iostream> #include <functional> struct Foo { inline Foo() { std::cout << "foo constructor" << std::endl; } inline ~Foo() { std::cout << "foo destructor" << std::endl; } }; void NotBroken() { Foo foo; std::function<void(Foo&)> callback; callback = [](auto&) { // <- 注意这里的auto& std::cout << "executed callback" << std::endl; }; callback(foo); } void TotallyBroken() { Foo foo; std::function<void(Foo&)> callback; callback = [](auto) { // <- 注意这里没有&的auto std::cout << "executed callback" << std::endl; }; callback(foo); } int main() { NotBroken(); std::cout << std::endl; TotallyBroken(); }
运行输出
foo constructor executed callback foo destructor foo constructor executed callback foo destructor foo destructor
问题
不带&的auto参数为何无法识别std::function所期望的引用类型?在MSVC、Clang和GCC中均存在此行为,额外的析构函数调用发生在Lambda执行结束时。
解答
这是C++泛型Lambda推导规则与std::function适配逻辑共同作用的结果,完全符合标准规范:
泛型Lambda的参数推导逻辑
泛型Lambda的auto参数推导和普通模板函数规则一致:当实参是引用类型时,推导会自动忽略引用属性。所以在[](auto)的场景中,传入的Foo&会被推导为Foo值类型,Lambda的实际参数类型就是Foo(值传递)。std::function的适配过程
std::function<void(Foo&)>要求可调用对象能接收Foo&类型参数,但此时Lambda只接受Foo值类型。为了完成调用,std::function会把传入的Foo&隐式拷贝构造出一个临时Foo对象,传递给Lambda执行。这个临时对象会在Lambda执行完毕后立即销毁,因此多出现了一次析构调用。带&的Lambda正常的原因
当使用[](auto&)时,auto会被推导为Foo,最终Lambda的参数类型是Foo&,完全匹配std::function的要求。调用时直接传递原对象的引用,不会产生临时对象,因此只有一次析构。
如果希望Lambda参数保持引用语义,必须显式在auto后添加&(或&&实现万能引用),否则必然会触发值拷贝。
内容的提问来源于stack exchange,提问作者ProgramGamer
相关产品推荐
相关产品推荐

