向Lambda按引用传递引用:f1与f2是否存在差异?
嘿,这个问题问得很细!虽然两个Lambda里的test看起来都是const std::string&,但它们的本质和行为差异可不小,咱们一步步说清楚:
首先先把你的代码贴出来方便对照:
void foo(const std::string& test){ auto f1 = [&test](){ std::cout << test << std::endl; }; f1(); auto f2 = [test](){ std::cout << test << std::endl; }; f2(); } int main() { std::string x = "x"; foo(x); }
核心差异在捕获方式,直接决定了test的本质
1. 引用捕获的f1:test是外部参数的“别名”
当你用[&test]捕获时,Lambda的闭包其实存了一个指向foo函数参数test的引用——而foo的test本身就是绑定到main里x的const std::string&。所以在f1里访问test时,你其实是直接在访问x的引用,此时test的类型确实是const std::string&。
但要注意风险:如果f1被保存下来,在foo函数执行完之后再调用,就会出现引用悬空的问题——毕竟foo的参数已经销毁了,这个引用就指向了无效内存。
2. 值捕获的f2:test是闭包自己持有的const副本
而[test]是值捕获,Lambda闭包里会创建一个**std::string类型的成员变量**,把foo参数test的值(也就是x的内容)拷贝一份存进去。
为什么你会觉得它是const std::string&?因为默认的Lambda是const成员函数(除非加mutable关键字),所以闭包里的这个test成员会被当成const std::string。当你在Lambda里用它的时候,编译器会自动做左值到引用的转换,但本质上它是闭包自己的常量值,和外部的x或者foo的test已经没关系了。
怎么直观验证这个差异?
用decltype加静态断言就能直接看到类型区别:
#include <type_traits> #include <iostream> #include <string> void foo(const std::string& test){ auto f1 = [&test](){ // 这里test的类型确实是const std::string& static_assert(std::is_same_v<decltype(test), const std::string&>, "f1 test is const ref"); std::cout << test << std::endl; }; f1(); auto f2 = [test](){ // 这里test的类型是const std::string,不是引用! static_assert(std::is_same_v<decltype(test), const std::string>, "f2 test is const value"); std::cout << test << std::endl; }; f2(); } int main() { std::string x = "x"; foo(x); }
这段代码能正常编译,直接坐实了两者的类型差异。
还有个行为上的差异:假如foo的参数是可修改的引用,在f1调用前修改外部的x,f1的输出会跟着变;但f2是值捕获,输出不会受外部修改的影响——因为它手里的是自己的副本。
内容的提问来源于stack exchange,提问作者user8024280

