将一组unique pointer的向量复制到新向量中

7
当我编译以下代码时,出现编译错误:
std::vector<std::unique_ptr<boxIndex>> tmpVec;
for(const auto& it: hrzBoxTmpMap){
    for(const auto& it2: hrzBoxVec){
        std::copy_if(hrzBoxVec.begin(), hrzBoxVec.end(), tmpVec.begin(), [&](std::unique_ptr<boxIndex>& p)
        {
            return !(it.second == p->getTop() &&
                     it.first != p->getLeft() );
        });
    }
}

编译错误为:
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_algo.h: 

> In instantiation of ‘_OIter std::copy_if(_IIter, _IIter, _OIter,
> _Predicate) [with _IIter = __gnu_cxx::__normal_iterator<std::unique_ptr<boxIndex>*, std::vector<std::unique_ptr<boxIndex> > >; _OIter =
> __gnu_cxx::__normal_iterator<std::unique_ptr<boxIndex>*, std::vector<std::unique_ptr<boxIndex> > >; _Predicate =
> smoothHrzIndexing(std::vector<std::unique_ptr<boxIndex>
> >&)::<lambda(std::unique_ptr<boxIndex>&)>]’: test_word_2.cpp:282:5:   required from here
> /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_algo.h:990:6:
> error: use of deleted functionstd::unique_ptr<_Tp, _Dp>&
> std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&)
> [with _Tp = boxIndex; _Dp = std::default_delete<boxIndex>]’ In file
> included from
> /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/memory:86:0,
>                  from test_word_2.cpp:8: /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/unique_ptr.h:263:19:
> error: declared here

有人能帮我解决这个问题吗?


7
你不能复制一个 unique_ptr,你需要将它们移动。 - quantdev
2
有趣的问题。我不知道如何使用标准算法来解决这个问题。有copy_if但没有move_if。你可以交换向量并在新向量上使用remove_if。 - Neil Kirk
2
只需按照@NeilKirk的建议交换和使用remove_if。 - juanchopanza
2
你不需要复制向量,交换速度很快。 - Neil Kirk
虽然通过正确移动指针可以解决这个问题,但有时确实需要进行复制。 在这里提供一些提示:https://dev59.com/HH_aa4cB1Zd3GeqP3nuf - DarioP
显示剩余5条评论
3个回答

7
你可以使用std::move_iterator<...>,例如以下示例(这是一个 SSCCE演示关键点):
#include <iostream>
#include <iterator>
#include <vector>
#include <memory>

int main()
{
    std::vector<std::unique_ptr<int>> boxVec;
    boxVec.emplace_back(new int(1));
    boxVec.emplace_back(new int(17));
    boxVec.emplace_back(new int(3));
    std::vector<std::unique_ptr<int>> tmpVec;
    std::copy_if(std::make_move_iterator(boxVec.begin()),
                 std::make_move_iterator(boxVec.end()),
                 std::back_inserter(tmpVec),
                 [&](std::unique_ptr<int> const& p){
                     return *p == 17; });
    for (auto const& x: boxVec) {
        (x? std::cout << *x: std::cout << "<null>") << " ";
    }
    std::cout << "\n";
}

解引用 std::move_iterator<It> 将返回迭代器值类型的适当右值。由于使用了 std::move(*it) 获取右值,因此它是一个引用。也就是说,在实际移动值之前,该值并未被窃取。比较使用 const&,即不会窃取该值。赋值将变成右值赋值。代码还使用 std::back_inserter() 来安排足够数量的元素放入目标中。

我认为这并不是一个稳定的解决方案,但我也认为没有像 std::move_if() 这样的算法(或任何结果相同的算法定制)。要真正处理有条件移动对象,我认为您需要针对传递到谓词中的值和分配对象的方式使用不同的访问机制(属性映射 将解决这些问题,但尚无提议将其添加到 C++ 标准中)。


1
你可以使用 remove_copy_if 替代 copy_if,这样移动的元素就可以从旧向量中删除。 - MatthiasB

3

没有直接的方法可以实现此操作,但您可以连接一些STL命令来实现您想要的效果:

  • std::stable_partitionstd::partition 来将容器分成两半。
  • std::move 将您想要移动到新向量中的值移动过去
  • vector.erase 删除旧的、无效的unique_ptr

最终,您将拥有一个干净的源向量(所有已移动、无效的条目都被删除)和一个干净的目标向量。

看起来可能是这样的:

std::vector<std::unique_ptr<int>> source, target;
// ... fill data
auto it = std::stable_partition(begin(source),end(source),[](std::unique_ptr<int> const& val) {
    return *val < 3; // your negated condition
});
std::move(it,end(source),std::back_inserter(target));
source.erase(it,end(source));

这里是一个实时示例


0

std::unique_ptr是可移动的,而不是可复制的。
因此,您不能使用std::copy_if()unique_ptr实例。

如果您真的想使用std::copy_if(),那么您可以使用std::shared_ptr代替std::unique_ptr,然后如果您想要摆脱旧的向量内容,只需使用例如vector::clear()销毁它。

或者,如果shared_ptr对您来说太过头重脚轻,或者在任何情况下您只想要一个unique_ptr的向量,则可以考虑使用std::remove_if()erase-remove习语来就地删除不需要的向量元素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接