我正在使用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循环中调用子类方法,并且它有助于覆盖。那么如何将矢量设置为彼此相等或者有更好的方法呢?
答案 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_if
和erase
,但这个习惯用法违反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::vector
和remove_erase
以及sort_unique_erase
。有趣的是,虽然上面的代码适用于binary_search
,std::vector
和std::list
,但我几乎总是使用std::deque
:但要将其编写为与任何顺序一起使用容器比编写容器更容易使用std::vector
。
设计这些容器算法的另一个选择是按值获取容器,并按值返回。这会强制一些std::vector
垃圾邮件,但在运行时基本上同样有效。