C++标准算法搭配lambda谓词使用std::move_iterator是否安全?
代码运行原理解析
你测试的代码如下:
std::vector<std::string> foo{"a","b","c"}; std::set<std::string> check{"a"}; std::vector<std::string> bar{"something_here"}; typedef std::vector<std::string>::iterator Iter; std::copy_if(std::move_iterator<Iter>(foo.begin()), std::move_iterator<Iter>(foo.end()), std::back_inserter(bar), [&check](std::string && s)->bool{return check.find(s) == check.end();});
核心认知纠正
首先要明确一个最容易混淆的C++规则:右值引用本身不会自动触发移动操作,它只是一个标记,代表被绑定的对象具备被移动的资格。只有当这个右值被用来初始化同类型新对象、作为参数传给移动赋值/移动构造函数、或者调用只接收右值的成员函数时,真正的资源所有权转移才会发生。
你之前的猜想存在两个核心误区:
- 不存在“foo内的string自动被移动到lambda中”的过程。lambda的形参
std::string&& s是一个右值引用类型的别名,直接绑定到当前迭代到的foo中的元素上,整个过程没有构造任何新的string对象,自然也不会在lambda参数传递阶段触发移动。 - 有名字的右值引用本身是左值,所以lambda内调用
check.find(s)时,匹配的是find接收const std::string&的重载,只是读取s的内容做集合查找,完全不会修改s、也不会转移s的资源所有权。lambda执行结束时,s作为引用类型的形参,本身不持有任何资源,出作用域销毁不会影响任何实际的string对象。
代码实际执行流程
std::copy_if配合std::move_iterator的实际执行逻辑是:
- 逐次迭代
move_iterator,解引用时得到foo内当前元素的右值引用 - 把这个右值引用绑定到lambda的形参s上,执行判断逻辑
- 如果lambda返回false:全程没有任何移动操作发生,对应元素完好留在foo中,进入下一轮迭代
- 如果lambda返回true:
copy_if会把当前拿到的右值引用,传给back_inserter生成的插入迭代器,这时候会触发string的移动构造,把foo内对应元素的资源直接转移给bar中新建的string对象——这个移动操作是标准库保证合法的,转移后bar中的元素独立持有资源,不存在悬空失效问题。
对你补充疑问的解答
你的判断只对了一半:
确实在lambda执行阶段,因为没有用传入的右值引用构造新的string对象,所以这一步没有发生移动,资源所有权没有变更。
但移动操作不是发生在lambda内部,而是发生在lambda返回true之后、copy_if往bar写入元素的阶段,这时候的移动是合法且安全的。
内容的提问来源于stack exchange,提问作者somewhat_clueless_developer
相关产品推荐
相关产品推荐

