简体   繁体   English

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

[英]std::vector::erase/remove_if resulting in nullptr

I have a vector of std::shared_ptr<MotionTask> objects from which I need to cleanse occasionally. 我有一个向量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); }));

The final assert is failing, as within the vector of tasks one is null (false). 最终assert失败,因为在任务向量中,一个为null(假)。

I do not understand how a call to erase could nullify a member. 我不了解erase呼叫如何会使成员无效。 I haven't been able to reproduce this in a unit test. 我无法在单元测试中重现此内容。

Is there an explanation that can be observed from the above code, and if not, what can I try to debug this? 从上面的代码中可以看到一个解释,如果没有,我该如何调试?

You are calling the single iterator std::vector::erase overload. 您正在调用单个迭代器std::vector::erase重载。 You need the two iterator version: 您需要两个迭代器版本:

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!!
);

The single iterator version removes a single element, whereas the erase-remove idiom requires a range to be removed. 单个迭代器版本删除单个元素,而“擦除删除”惯用语则需要删除范围。 This is achieved using the two iterator version. 这是使用两个迭代器版本实现的。

Your problem is that you are using the single iterator version of vector<T>::erase , which erases one element. 您的问题是您正在使用vector<T>::erase的单个迭代器版本,该版本会擦除一个元素。

There are two approaches to fix this. 有两种方法可以解决此问题。 The first is to use the two iterator version of vector<T>::erase . 第一种是使用vector<T>::erase的两个迭代器版本。 The second is to not use iterator based algorithms, and start writing container based algorithms. 第二个是不使用基于迭代器的算法,而是开始编写基于容器的算法。

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);
}

which does the two operations of removing elements and erasing them in one pass. 一键执行删除元素和擦除它们的两项操作。 Traits classes can be written to allow this to not only work with vector , but also associative containers like set and map . 可以编写Traits类,以使其不仅可以使用vector ,还可以使用setmap等关联容器。

A similar one I find useful is sort_unique_erase , which takes a collection and removes duplicates. 我发现类似的一个有用的是sort_unique_erase ,它接受一个集合并删除重复项。

By writing these kind of container-based algorithms, your code both becomes clearer and less error prone, because you aren't repeating yourself all over the place. 通过编写这些基于容器的算法,您的代码将变得更加清晰,并且不易出错,因为您不会在各处重复自己的代码。 Many iterator based techniques silently fail in unexpected ways with a simple typo: a tested container based algorithm can reliably work on any container passed in, or fail to compile. 许多基于迭代器的技术都会通过简单的输入错误地以意想不到的方式失败:经过测试的基于容器的算法可以可靠地对传入的任何容器进行工作,或者无法编译。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM