【问题标题】:Erasing from a std::vector while doing a for each?在为每个执行一次时从 std::vector 中擦除?
【发布时间】:2010-10-15 01:30:03
【问题描述】:

迭代的正确方法是使用迭代器。但是,我认为通过擦除,迭代器无效。

基本上我想做的是:

for(iterator it = begin; it != end; ++it)
{
    if(it->somecondition() )
    {
     erase it
    }

}

如果没有 v[i] 方法,我怎么能做到这一点?

谢谢

struct RemoveTimedEvent
{
    bool operator()(const AguiTimedEvent& pX, AguiWidgetBase* widget) const 
    {
        return pX.getCaller() == widget;
    }
};

void AguiWidgetContainer::clearTimedEvents( AguiWidgetBase* widget )
{
    std::vector<AguiTimedEvent>::iterator it = std::remove_if(timedEvents.begin(),
        timedEvents.end(), RemoveTimedEvent());
    timedEvents.erase(it, timedEvents.end());

}

【问题讨论】:

    标签: c++ vector


    【解决方案1】:

    erase() 返回一个新的迭代器:

    for(iterator it = begin; it != end(container) /* !!! */;)
    {
        if (it->somecondition())
        {
            it = vec.erase(it);  // Returns the new iterator to continue from.
        }
        else
        {
            ++it;
        }
    }
    

    请注意,我们不能再将其与预先计算的结束进行比较,因为我们可能会删除它并因此使其无效。我们每次都必须明确地结束。

    更好的方法可能是将std::remove_iferase() 结合起来。你从 O(N2) (每个元素都会被擦除和移动)变为 O(N):

    iterator it = std::remove_if(begin, end, pred);
    vec.erase(it, vec.end());
    

    其中pred 是您的删除谓词,例如:

    struct predicate // do choose a better name
    {
        bool operator()(const T& pX) const // replace T with your type
        {
            return pX.shouldIBeRemoved();
        }
    };
    
    iterator it = std::remove_if(begin, end, predicate());
    vec.erase(it, vec.end());
    

    在你的情况下,你可以让它很笼统:

    class remove_by_caller
    {
    public:
        remove_by_caller(AguiWidgetBase* pWidget) :
        mWidget(pWidget)
        {}
    
        // if every thing that has getCaller has a base, use that instead
        template <typename T> // for now a template
        bool operator()(const T& pX) const
        {
            return pX.getCaller() == mWidget;
        }
    
    private:
        AguiWidgetBase* mWidget;
    };
    
    std::vector<AguiTimedEvent>::iterator it =
        std::remove_if(timedEvents.begin(), timedEvents.end(), remove_by_caller(widget));
    timedEvents.erase(it, timedEvents.end());
    

    注意 lambda 的存在是为了简化这个过程,无论是在 Boost 还是 C++11 中。

    【讨论】:

    • 我不确定我是否理解第一种方法是N平方的,为什么不是N,我只迭代一次以删除项目。
    • 当然,除非擦除返回的迭代器将您带回顶部
    • @Milo:这是 N^2。如果您删除第一个元素,所有其他元素将被复制下来以取而代之。这样做 N 次,你已经触摸了每一个 N^2 次。您可以将参数传递给谓词,谓词会转发它。
    • 第一个 sn-p 可能是错误的,因为 end 迭代器在擦除后会失效。
    • 我不会在每次循环迭代时重新评估 end(),就在调用 erase() 时:for(iterator it = vec.begin(), end = vec.end(); it != end;) { ... if (condition) { it = vec.erase(it); end = vec.end(); } ... }
    猜你喜欢
    • 2011-07-17
    • 2011-10-24
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-11
    相关资源
    最近更新 更多