我有以下模式:
std::vector包含对象的原始指针(我知道原始指针是"邪恶的",但它是需要维护的遗留软件).伪代码:
for each pointer in vector
{
if (SomeTest(pointer))
{
DoSomething(pointer)
delete pointer
remove pointer from vector
}
}
Run Code Online (Sandbox Code Playgroud)
我无法想出一些干净利落的代码.
这个链接提供了不同的方法,但它们看起来或多或少都很麻烦.
我现在使用的繁琐解决方案:
for(auto & p : v)
{
if (SomeTest(p))
{
DoSomething(p);
delete p;
p = nullptr;
}
}
v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
Run Code Online (Sandbox Code Playgroud)
YSC*_*YSC 28
通常答案是:了解你<algorithm>的(并且是对我自己的好提醒);)
std::partition是你在找什么:std::partition(begin, end, p)"动作"范围内的元素[ begin,end),这不符合谓词p的范围的结束; 然后,您可以将它们视为批处理.
auto const to_be_removed = std::partition(begin(v), end(v), [](auto p){ /* predicate */ });
std::for_each(to_be_removed, end(v), [](auto p) {
/* crunch */
delete p;
});
v.erase(to_be_removed, end(v));
Run Code Online (Sandbox Code Playgroud)
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector v = { new int{0}, new int{1}, new int{2} };
// let's delete all even values
auto const to_be_removed = std::partition(begin(v), end(v), [](auto p){ return *p % 2 != 0; });
std::for_each(to_be_removed, end(v), [](auto p) {
std::cout << "Deleting value " << *p << "...\n";
delete p;
});
v.erase(to_be_removed, end(v));
}
Run Code Online (Sandbox Code Playgroud)
这种实现有两个主要缺点:来自向量的顺序不稳定(1),它可以被考虑到可重用函数(2)中.
std::stable_partition.template<class InputIt, class UnaryPredicate, class UnaryDeleter>
InputIt delete_if(InputIt begin, InputIt end, UnaryPredicate p, UnaryDeleter d)
{
auto const to_be_removed = std::stable_partition(begin, end, std::not_fn(p));
std::for_each(to_be_removed, end, [d](auto p) { d(p) ; delete p; });
return to_be_removed;
}
template<class Container, class UnaryPredicate, class UnaryDeleter>
auto delete_if(Container& c, UnaryPredicate p, UnaryDeleter d)
{
using std::begin, std::end;
return c.erase(delete_if(begin(c), end(c), p, d), end(c));
}
Run Code Online (Sandbox Code Playgroud)
用法:
delete_if(v, SomeTest, DoSomething);
Run Code Online (Sandbox Code Playgroud)
Gal*_*lik 13
您可以使用std::remove_if我不知道为什么您链接的文章std::remove_if 在删除指针之前使用,因为这不起作用.您必须在删除之前删除指针:
std::vector<int*> v;
v.erase(std::remove_if(std::begin(v), std::end(v), [](int* p){
// do your test and do not remove on failure
if(!SomeTest(p))
return false; // keep this one
DoSomething(p);
// Now we remove but be sure to delete here, before the
// element is moved (and therefore invalidated)
delete p;
return true; // signal for removal
}), std::end(v));
Run Code Online (Sandbox Code Playgroud)
注意:为什么这是安全的.
删除指针不会修改指针本身,而是修改指向的对象.这意味着没有使用这种方法修改元素.
在该标准C++17 28.6.8 5保证了谓词将只是一次为每个元素调用.
最简单的解决方案 - 从链接文章开始 - 是采用该erase_if功能
template <typename Container, typename Pred>
void erase_if(Container &c, Pred p)
{
c.erase(std::remove_if(std::begin(c), std::end(c), p), std::end(c));
}
Run Code Online (Sandbox Code Playgroud)
然后用它来调用它
erase_if(v, [](T *pointer)
{
if (SomeTest(pointer))
{
DoSomething(pointer);
delete pointer;
return true; //remove pointer from vector
}
return false;
});
Run Code Online (Sandbox Code Playgroud)
如果要将SomeTest/DoSomething部分与delete部分分开,您显然可以将谓词拆分为两个:
template <typename Container, typename Pred>
void delete_if(Container &c, Pred p)
{
auto e = std::remove_if(std::begin(c), std::end(c),
[&p](Container::value_type *pointer)
{
if (p(pointer)) {
delete pointer;
return true;
}
return false;
});
c.erase(e, std::end(c));
}
Run Code Online (Sandbox Code Playgroud)
既然你没有说过为什么你不喜欢erase_if你自己联系,我猜不出这是否有同样的问题.