以便宜的方式从任意容器中删除元素的惯用法?

时间:2017-08-03 08:48:10

标签: c++ containers c++-standard-library idiomatic

所有C ++标准库容器都有insert()方法;然而,他们并不是都有remove()方法,它不接受任何参数,但以任意顺序执行最便宜的删除。现在,当然这对于不同的容器会有不同的行为:在向量中我们将从后面移除,在单个列表中我们将从前面移除(除非我们保留指向尾部的指针),依此类推实施细节。

那么除了为每个容器滚动我自己的模板专业化之外,还有更惯用的方法吗?

2 个答案:

答案 0 :(得分:0)

标准容器的部分设计是,如果可以为该容器提供最佳(通过选择的措施),它们仅提供作为成员功能的操作。

如果容器提供成员函数,那是因为有一些实现该函数的方法是对该容器最佳的方式。

如果无法提供操作的最佳实现(如remove()),则不提供。

std::list和(C ++ 11及更高版本)std::forward_list旨在有效删除元素,这就是为什么它们是唯一具有remove()成员函数的容器。< / p>

其他容器不是为有效去除任意元素而设计的;

  • std::array无法调整大小,因此无论是insert()还是remove()成员函数都没有意义。
  • std::deque仅针对开头或结尾处的移除进行了优化。
  • std::vector删除元素的效率低于其他容器,除非(可能)从最后开始。

因此,为这些容器实现remove()成员函数违背了设计理念。

因此,如果您希望能够有效地从容器中删除元素,则需要为该作业选择正确的容器。

滚动自己的标准容器包装,模仿某些容器不支持的操作只会产生误导 - 从鼓励包装类的用户认为他们不需要是如果他们对性能或内存使用有特殊要求,请小心选择容器。

答案 1 :(得分:0)

所以回答你的问题

“那么,除了为每个容器滚动我自己的模板专业化之外,还有更惯用的方法吗?

有很多方法可以删除

  1. 序列容器和无序容器的erase()返回下一个 擦除项目后的迭代器。

  2. 关联容器的erase()不返回任何内容。

  3. / *  * 从Vector或Deque中删除  * /

     vector<int> vec = {1, 4, 1, 1, 1, 12, 18, 16}; // To remove all '1'
      for (vector<int>::iterator itr = vec.begin(); itr != vec.end(); ++itr) {
         if ( *itr == 1 ) {
            vec.erase(itr);
         }
      }   // vec: { 4, 12, 18, 16}
      // Complexity: O(n*m)
    
      remove(vec.begin(), vec.end(), 1);  // O(n) 
                                          // vec: {4, 12, 18, 16, ?, ?, ?, ?}
    
    
    
    
      vector<int>::iterator newEnd = remove(vec.begin(), vec.end(), 1);   // O(n)
      vec.erase(newEnd, vec.end());  
    
      // Similarly for algorithm: remove_if() and unique()
    
    
      // vec still occupy 8 int space: vec.capacity() == 8
      vec.shrink_to_fit();   // C++ 11
      // Now vec.capacity() == 4 
    
      // For C++ 03:
      vector<int>(vec).swap(vec); // Release the vacant memory
    

    / *  * 从列表中删除  * /

      list<int> mylist = {1, 4, 1, 1, 1, 12, 18, 16};
    
      list<int>::iterator newEnd = remove(mylist.begin(), mylist.end(), 1);  
      mylist.erase(newEnd, mylist.end());
    
    
      mylist.remove(1);  // faster
    

    / *  * 从关联容器或无序容器中删除  * /

      multiset<int> myset = {1, 4, 1, 1, 1, 12, 18, 16};
    
      multiset<int>::iterator newEnd = remove(myset.begin(), myset.end(), 1);  
      myset.erase(newEnd, myset.end()); // O(n)
    
      myset.erase(1); // O(log(n)) or O(1)