为何这些C++ Lambda会复制字符串并触发段错误?
std::function返回引用时的拷贝问题解析
先看测试代码:
#include <functional> #include <iostream> #include <string> class Foo { public: const std::string& GetData() { return m_data; } std::string m_data = "foo data"; }; std::string s_data = "hello"; const std::string& GetData() { return s_data; } int main() { // Trial #1 - Seg faults. String copied. //std::function<const std::string&()> getter1 = [] { return GetData(); }; //std::cout << "data = " << getter1() << std::endl; // Trial #2 - Seg faults. String copied. //auto* foo = new Foo(); //std::function<const std::string&()> getter2 = [foo] { return foo->GetData(); }; //std::cout << "data = " << getter2() << std::endl; //delete foo; // Trial #3 - Works auto getter3 = [] { return GetData(); }; std::cout << "data = " << getter3() << std::endl; // Trial #4 - Works auto* foo2 = new Foo(); auto getter4 = [foo2] { return foo2->GetData(); }; std::cout << "data = " << getter4() << std::endl; delete foo2; return 0; }
试验1、2触发段错误的核心原因
问题出在Lambda的返回类型推导规则上:
- 虽然
GetData()返回的是const std::string&,但Lambda默认会把返回类型推导成值类型std::string,这就会触发一次字符串拷贝,生成临时对象。 - 当把这个Lambda赋值给
std::function<const std::string&()>时,std::function会尝试返回临时对象的引用,但临时对象的生命周期只到Lambda执行结束。等后续调用getter1()或getter2()时,引用指向的对象已经销毁,直接触发段错误。
试验3、4正常运行的原因
用auto推导getter3和getter4的类型时,编译器会根据Lambda的实际返回类型(推导后的std::string)生成对应闭包类型。调用时返回的是拷贝后的字符串对象,这个对象的生命周期足够支撑到cout输出完成,所以不会出问题。你的猜测没错,这两个getter的实际返回类型确实是std::string,而非引用。
修复试验1、2的方法
显式指定Lambda的返回类型为引用,让它直接返回原对象的引用而非拷贝:
// 修复试验1 std::function<const std::string&()> getter1 = []() -> const std::string& { return GetData(); }; // 修复试验2(注意:要保证foo在getter2使用前不被销毁,原代码里delete foo后调用仍会出错) auto* foo = new Foo(); std::function<const std::string&()> getter2 = [foo]() -> const std::string& { return foo->GetData(); }; std::cout << "data = " << getter2() << std::endl; delete foo;
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

