类成员函数中Lambda按值捕获出现异常调用行为的原因咨询
为啥按值捕获时拷贝构造函数被调用了5次?
嘿,这个问题我刚看到的时候也有点懵,拆解下来就清楚了,咱们一步步捋:
先看前3次拷贝的来源
你初始化catalog<item> c({item(10),item(20),item(30)})的时候,std::initializer_list里的三个临时item对象,会被逐个拷贝到catalog内部的std::list<T>容器里——这就是程序输出中分割线之前的3次拷贝构造函数调用,属于容器初始化的正常行为。
重点:分割线后的5次拷贝是怎么来的?
这才是你疑惑的核心,问题出在Lambda按值捕获+无优化编译的组合上:
- Lambda捕获时的第一次拷贝:当你用
[e]按值捕获函数参数e时,Lambda的闭包对象会创建一个item的拷贝,这是分割线后的第1次调用。 - std::find_if对谓词的多次拷贝:在默认的无优化编译模式(比如
-O0)下,std::find_if的实现可能会多次拷贝传入的谓词(也就是你的Lambda对象)。而Lambda闭包里包含了一个item的拷贝,每拷贝一次Lambda,就会连带拷贝里面的item成员——这就产生了剩下的4次调用,加起来正好是5次。
至于按引用捕获为啥没问题?因为[&e]捕获的是e的引用,闭包里不会创建item的拷贝,自然不会触发拷贝构造函数,完全符合你的预期。
怎么解决这个问题?
给你几个可行的方案:
- 开启编译器优化:如果用GCC/Clang,加上
-O2编译选项;用MSVC的话开启/O2。优化器会消除那些不必要的拷贝,此时按值捕获的拷贝次数会降到1次左右,和你预期的一致。 - 添加移动构造函数:给
item类加上移动构造函数,这样当需要转移对象时,会优先调用移动构造而非拷贝,既减少开销,也能看到更清晰的输出:item(item&& i) noexcept : price{i.price} { std::cout << "move-constructor called\n"; } - 优先按引用捕获(安全前提下):像你现在的场景,
e是isLarger的参数,生命周期完全覆盖std::find_if的执行过程,按引用捕获是安全的,不会有悬空引用的问题,还能避免所有拷贝。
内容的提问来源于stack exchange,提问作者nae9on
相关产品推荐
相关产品推荐

