遍历 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_ptrs。问题:指针是唯一的,因此我无法将它们复制到助手集合中。

我可以在 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 不提供对其元素的可变访问,任何类型的 transformremove 都不起作用。您将必须构建一个迭代器容器,然后在处理完集合后遍历该迭代器容器,为每个迭代器调用 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
});