如何将两个向量<unique_ptr <...>&gt;设置为彼此相等?</unique_ptr <...>

时间:2013-06-19 23:08:33

标签: c++ visual-studio-2012 vector unique-ptr

我正在使用Visual Studio 2012 C ++,我想设置两个具有相同指针的向量。

    using namespace std;
    vector<unique_ptr<Unit>> unitVector;
    vector<unique_ptr<Unit>> nonDeadUnits;

    .... (stuff added to unitVector) ....

    for (auto unit = unitVector.begin(); unit != unitVector.end(); ++unit) {
            if ((*unit)->health > 0) {
                    nonDeadUnits.push_back(*unit);
            }
    }

    unitVector.clear();
    unitVector = nonDeadUnits; // error here (see below error code)

我想删除所有生命值小于0的单位,但是如果我尝试直接从向量中删除它们,我会尝试访问我不应该访问的内存,从而终止程序。这就是为什么我选择这样做的原因。唯一的问题是unique_ptr不允许我想要的复制类型。这是错误:

    error C2248: 'std::unique_ptr<_Ty>::operator =' : cannot access private member declared in class 'std::unique_ptr<_Ty>' c:\program files (x86)\microsoft visual studio 11.0\vc\include\xutility 2089

我想拥有unique_ptr,因为向量稍后会在for循环中调用子类方法,并且它有助于覆盖。那么如何将矢量设置为彼此相等或者有更好的方法呢?

3 个答案:

答案 0 :(得分:8)

一般的想法是使用std::remove_if来交换unitsVector中的元素,然后一旦所有死单位都在向量的末尾,你就把它们砍掉。

#include <memory>
#include <vector>

struct Unit {
    int health;
};

// The non-working version.
//
// void remove_dead_units(std::vector<std::unique_ptr<Unit>> &unitVector)
// {
//     std::vector<std::unique_ptr<Unit>> nonDeadUnits;
//     for (auto unit : unitVector)
//         if (unit->health > 0)
//             nonDeadUnits.push_back(unit);
//     unitVector = nonDeadUnits;
// }

void remove_dead_units(std::vector<std::unique_ptr<Unit>> &unitVector)
{
    auto isDead = [](const std::unique_ptr<Unit> &u) -> bool { return (u->health <= 0); };
    auto newEnd = std::remove_if(unitVector.begin(), unitVector.end(), isDead);
    unitVector.erase(newEnd, unitVector.end());
}

我确信还有其他方法可以做到这一点,更接近你所尝试的内容(编辑:实际上KerrekSB刚刚发布了一个,只使用了一个std::move和一个swap);但我认为“shuffle and chop”方法更现代 - C ++ ish。

答案 1 :(得分:5)

也许以下逻辑会更简单:

vector<unique_ptr<Unit>> unitVector = /* ... */;
vector<unique_ptr<Unit>> nonDeadUnits;

for (auto & p : unitvector)
{
    if (p->health > 0) { nonDeadUnits.push_back(std::move(p)); }
}

unitVector.swap(nonDeadUnits);

否则,标准的删除 - 擦除习惯用语可能更主流:

unitVector.erase(remove_if(unitVector.begin(), unitVector.end(),
                           [](unique_ptr<Unit> const & p) -> bool { return p->health <= 0; }),
                 unitVector.end());

答案 2 :(得分:1)

快速执行此操作的方法是使用remove_iferase,但这个习惯用法违反DRY(不要重复自己),我看到人们在使用它时会犯一些细微的错误(忘了第二次)通过({1}}传递(不充分)测试用例的迭代器,然后在生产中失败!)

我对这类问题的解决方案 - 为某些属性过滤erase - 就是编写一个基于容器的算法来为我做这个。

std::vector

现在您有一个基于template<typename SeqContainer, typename Lambda> SeqContainer&& remove_erase_if( SeqContainer&& c, Lambda&& test ) { using std::begin; using std::end; auto new_end = std::remove_if( begin(c), end(c), std::forward<Lambda>(test) ); c.erase( new_end, end(c) ); return std::forward<SeqContainer>(c); } 的容器,我们可以过滤列表:

remove_erase_if

......就是这样。已从// const & is important, we don't want to copy a `unique_ptr` remove_erase_if( unitVector, [&]( std::unique_ptr<Unit> const& unit ) { return (unit->health() <= 0); }); 删除health() <= 0的所有内容。

我发现我经常使用的其他有用的基于容器的算法包括std::vectorremove_erase以及sort_unique_erase。有趣的是,虽然上面的代码适用于binary_searchstd::vectorstd::list,但我几乎总是使用std::deque:但要将其编写为与任何顺序一起使用容器比编写容器更容易使用std::vector

设计这些容器算法的另一个选择是按值获取容器,并按值返回。这会强制一些std::vector垃圾邮件,但在运行时基本上同样有效。