遍历 std::set<unique_ptr>,如何跟踪哪些要删除?
Iterating over std::set<unique_ptr>, how to keep track which ones to remove?
我需要遍历 class T
的一些对象。
它们存储在 std::set<std::unique_ptr<T>> tees
。
循环体的主要目的是使用对象,但通过这样做我也会发现何时不再需要某些对象并可以将其删除。
我正在使用基于范围的 for 循环迭代 unique_ptrs:
for (std::unique_ptr<T> & tee : tees)
我知道我不能在循环 (UB) 内调用 tees.erase(tee)。因此,我应该收集需要在助手集合中删除的 unique_ptr
s。问题:指针是唯一的,因此我无法将它们复制到助手集合中。
我可以在 std::set<T*>
中收集原始指针,但我如何在循环后使用这些指针从 tees
集合中删除匹配的 unique_ptr
?另外,当我在这个问题上努力使用智能指针时,再次收集原始指针感觉不对。
我可以切换到 shared_ptr
,但指针只会在删除对象时共享。感觉不对。
我可以从基于范围的 for 切换到其他东西,比如自己处理迭代器,并在删除条目之前获取下一个迭代器。但是回到 C++11 之前的技术也感觉不对。
我可以切换到 std::remove_if。 (编辑:实际上我不能。在这个问题下方和接受的答案下方的评论中进行了解释。)循环的主体将移入 unary_predicate lambda。但是循环的主要目的不是确定对象是否应该被删除,而是利用它们,改变它们。
阻力最小的方法似乎是回到迭代器处理,那样我什至不需要辅助集合。但我想知道你是否可以帮助我提供 C++11-ish(或 14,17)解决方案?
我认为您找不到比
更容易的事情了
for(auto it = container.begin(), it != container.end();)
{
//use *it here
if(needs_to_be_erased)
it = container.erase(it);
else
++it;
}
因为 std::set
不提供对其元素的可变访问,任何类型的 transform
或 remove
都不起作用。您将必须构建一个迭代器容器,然后在处理完集合后遍历该迭代器容器,为每个迭代器调用 erase
。
我认为您可以将这些位置复制到一个新的数据结构中,然后通过以相反的顺序访问新的数据结构,在另一个循环中删除这些项目。
int counter =0;
vector<int> indices;
for (unique_ptr<T> & tee : tees)
{
if (bCondition)
indices.push_back(counter);
counter++;
}
reverse(indices.begin(), indices.end());
for (int i : indices)
tees.erase(tees.begin() + i);
不完全是一个解决方案,但如果您必须经常这样做,您可以为它制定自己的算法。我想这不在标准库中的原因是算法需要知道 container 来执行 erase.
所以你可以这样做:
template<typename Cont, typename Pred>
void erase_if(Cont& c, decltype(std::begin(c)) b, decltype(std::end(c)) e, Pred p)
{
while(b != e)
{
if(p(*b))
b = c.erase(b);
else
++b;
}
}
template<typename Cont, typename Pred>
void erase_if(Cont& c, Pred p)
{ erase_if(c, std::begin(c), std::end(c), p); }
然后将其命名为:
erase_if(tees, [](std::unique_ptr<int> const& up){
// use up here...
return (*up) & 1; // erase if odd number
});
或
erase_if(tees, std::begin(tees), std::end(tees), [](std::unique_ptr<int> const& up){
// use up here...
return (*up) & 1; // erase if odd number
});
我需要遍历 class T
的一些对象。
它们存储在 std::set<std::unique_ptr<T>> tees
。
循环体的主要目的是使用对象,但通过这样做我也会发现何时不再需要某些对象并可以将其删除。
我正在使用基于范围的 for 循环迭代 unique_ptrs:
for (std::unique_ptr<T> & tee : tees)
我知道我不能在循环 (UB) 内调用 tees.erase(tee)。因此,我应该收集需要在助手集合中删除的 unique_ptr
s。问题:指针是唯一的,因此我无法将它们复制到助手集合中。
我可以在 std::set<T*>
中收集原始指针,但我如何在循环后使用这些指针从 tees
集合中删除匹配的 unique_ptr
?另外,当我在这个问题上努力使用智能指针时,再次收集原始指针感觉不对。
我可以切换到 shared_ptr
,但指针只会在删除对象时共享。感觉不对。
我可以从基于范围的 for 切换到其他东西,比如自己处理迭代器,并在删除条目之前获取下一个迭代器。但是回到 C++11 之前的技术也感觉不对。
我可以切换到 std::remove_if。 (编辑:实际上我不能。在这个问题下方和接受的答案下方的评论中进行了解释。)循环的主体将移入 unary_predicate lambda。但是循环的主要目的不是确定对象是否应该被删除,而是利用它们,改变它们。
阻力最小的方法似乎是回到迭代器处理,那样我什至不需要辅助集合。但我想知道你是否可以帮助我提供 C++11-ish(或 14,17)解决方案?
我认为您找不到比
更容易的事情了for(auto it = container.begin(), it != container.end();)
{
//use *it here
if(needs_to_be_erased)
it = container.erase(it);
else
++it;
}
因为 std::set
不提供对其元素的可变访问,任何类型的 transform
或 remove
都不起作用。您将必须构建一个迭代器容器,然后在处理完集合后遍历该迭代器容器,为每个迭代器调用 erase
。
我认为您可以将这些位置复制到一个新的数据结构中,然后通过以相反的顺序访问新的数据结构,在另一个循环中删除这些项目。
int counter =0;
vector<int> indices;
for (unique_ptr<T> & tee : tees)
{
if (bCondition)
indices.push_back(counter);
counter++;
}
reverse(indices.begin(), indices.end());
for (int i : indices)
tees.erase(tees.begin() + i);
不完全是一个解决方案,但如果您必须经常这样做,您可以为它制定自己的算法。我想这不在标准库中的原因是算法需要知道 container 来执行 erase.
所以你可以这样做:
template<typename Cont, typename Pred>
void erase_if(Cont& c, decltype(std::begin(c)) b, decltype(std::end(c)) e, Pred p)
{
while(b != e)
{
if(p(*b))
b = c.erase(b);
else
++b;
}
}
template<typename Cont, typename Pred>
void erase_if(Cont& c, Pred p)
{ erase_if(c, std::begin(c), std::end(c), p); }
然后将其命名为:
erase_if(tees, [](std::unique_ptr<int> const& up){
// use up here...
return (*up) & 1; // erase if odd number
});
或
erase_if(tees, std::begin(tees), std::end(tees), [](std::unique_ptr<int> const& up){
// use up here...
return (*up) & 1; // erase if odd number
});