为何Lambda中声明为const的指针指向会发生变化?
代码示例
#include <iostream> struct A { int i; }; class AProxy { private: A* const m_stored; public: AProxy(A& stored) : m_stored(&stored) {} void print() { std::cout << "i is " << m_stored->i << std::endl; } }; class Holder { private: A& m_stored1; A& m_stored2; public: Holder(A& stored1, A& stored2) : m_stored1(stored1), m_stored2(stored2) {} void print() { std::cout << "i is " << m_stored1.i << std::endl; } void swap() { std::swap(m_stored1, m_stored2); } AProxy getstored1() { return {m_stored1}; } AProxy getstored2() { return {m_stored2}; } }; int main() { A foo; A bar; foo.i = 5; bar.i = 6; Holder holder = {foo, bar}; AProxy fooproxy = holder.getstored1(); AProxy barproxy = holder.getstored2(); auto foofunc = [fooproxy]() { AProxy copy = {fooproxy}; copy.print(); }; foofunc(); //Prints 5 holder.swap(); foofunc(); //Prints 6? return 0; }
疑问
我在AProxy中声明m_stored为指向不可变的const指针,预期它会持续指向foo,但调用holder.swap()后,Lambda中的输出变为了6,这是为什么?
解答
你误解了A* const m_stored的含义,以及Holder::swap()实际做的事情:
A* const m_stored的本质:
这个声明表示指针变量m_stored本身是不可修改的——也就是说,它一旦指向某个A对象的地址,就不能再指向其他地址。但它指向的对象的内容是可以被修改的,比如对象的i成员变量。Holder::swap()的实际操作:Holder里的m_stored1和m_stored2是A类型的引用,C++中引用一旦绑定到对象就无法改变指向。当你调用std::swap(m_stored1, m_stored2)时,std::swap并没有交换引用的指向,而是交换了两个引用所指向的对象的全部内容——也就是把foo和bar的i值(以及A的所有成员)做了交换。为什么输出会变6:
你的fooproxy里的m_stored确实一直指向foo对象的地址,但调用holder.swap()后,foo对象的i值已经被换成了原来bar的6,而bar的i值变成了5。Lambda捕获的fooproxy副本依然指向foo,所以第二次调用foofunc()时,打印的是foo现在的i值6。
如果想要验证这个结论,可以在holder.swap()之后加上两行打印:
std::cout << "foo.i = " << foo.i << std::endl; std::cout << "bar.i = " << bar.i << std::endl;
你会看到输出是foo.i = 6和bar.i = 5,这就直接证明了对象内容被交换,而指针指向的地址并没有变。
内容的提问来源于stack exchange,提问作者Brad

