std :: vector :: erase / remove_if导致nullptr

时间:2013-06-09 23:22:50

标签: c++ stdvector

我有一个std::shared_ptr<MotionTask>个对象的向量,偶尔我需要清理它。

// this assert passes
assert(std::all_of(d_tasks.begin(), d_tasks.end(),
       [](shared_ptr<MotionTask> task) { return bool(task); }));

// Remove any committed tasks for which the corresponding module has completed
d_tasks.erase(
  remove_if(
    d_tasks.begin(), 
    d_tasks.end(),
    [module](shared_ptr<MotionTask> const& task)
    {
      return task->isCommitted() && task->getModule() == module;
    }
  )
);

// this assert fails
assert(std::all_of(d_tasks.begin(), d_tasks.end(),
       [](shared_ptr<MotionTask> task) { return bool(task); }));

最终assert失败,因为在任务向量中,一个为null(false)。

我不明白对erase的调用如何使成员无效。我无法在单元测试中重现这一点。

是否可以从上面的代码中看到解释,如果没有,我可以尝试调试它?

2 个答案:

答案 0 :(得分:5)

您正在调用单个迭代器std::vector::erase重载。您需要两个迭代器版本:

d_tasks.erase(
  remove_if(
    d_tasks.begin(), 
    d_tasks.end(),
    [module](shared_ptr<MotionTask> const& task)
    {
      return task->isCommitted() && task->getModule() == module;
    }
  ),
  d_tasks.end() // HERE!!
);

单个迭代器版本删除单个元素,而擦除删除习惯用法需要删除范围。这是使用两个迭代器版本实现的。

答案 1 :(得分:2)

您的问题是您正在使用vector<T>::erase的单个迭代器版本,它会删除一个元素。

有两种方法可以解决这个问题。第一种是使用vector<T>::erase的两个迭代器版本。第二种是不使用基于迭代器的算法,并开始编写基于容器的算法。

template<typename Container, typename Lambda>
Container&& remove_if_erase( Container&& container, Lambda&& closure ) {
  using std::begin; using std::end;
  container.erase(
    std::remove_if(
      begin(container), end(container), std::forward<Lambda>(closure)
    ),
    end(container)
  );
  return std::forward<Container>(container);
}

执行删除元素和一次擦除它们的两个操作。可以编写特征类,使其不仅可以使用vector,还可以使用setmap等关联容器。

我觉得有用的类似的是sort_unique_erase,它会收集并删除重复项。

通过编写这些基于容器的算法,您的代码变得更加清晰,不易出错,因为您不会在整个地方重复自己。许多基于迭代器的技术以一种简单的拼写错误以意想不到的方式无声地失败:基于测试容器的算法可以可靠地处理传入的任何容器,或者无法编译。