在STL实现中是否存在中心分配双端队列或向量?

时间:2014-07-08 18:52:25

标签: c++ c++11 vector stl deque

我正在阅读deque s vs vector s,并遇到了wikipedia entry,其中说deque使用动态数组的三种可能实现之一是:

  

从底层数组的中心分配deque内容,和   到达任一端时调整基础数组的大小。这个   方法可能需要更频繁的调整并浪费更多空间,   特别是当元素仅插入一端时。

我想知道是否有任何实际使用此中心分配策略的STL(或STL风格)实现?

我在问这个策略看起来很吸引人,因为它只涉及一个底层数组,从而消除了内存不连续性问题,这可能是dequevector相比时唯一的主要问题}。如果我理解正确,这很可能是std::vector的替代,它允许O(1)pop_front(摊销)或deque的替换,具有内存连续性保证。我认为这是以std::vector的缓冲空间加倍为代价的,这不是我用例的主要问题。

此外,在这样的容器中间插入/删除平均需要std::vector的一半时间是真的吗?


更新:

正如@Lightness Races in Orbit指出的那样,这样的实施将不会在现行标准下使用,因为没有人可以从STL的每份合同的优势中受益,并且每个人都会受到不利影响。关于缺点的另一个问题是:

是否可以实现 vectordeque类容器(比如说bivector),以便除了{{的功能/运算符之外1}},

1)提供(摊销的)恒定时间std::vectorpush_front()操作和

2)在增加大小后保证内存连续但不保证迭代器有效性?

我想在场景后面有一个数组,pop_front()上的很多间接/性能问题都会消失。

2 个答案:

答案 0 :(得分:3)

没有标准库(不是“STL”)实现会对此感到烦恼,因为它有你提到的缺点,而且好处不是std::deque的要求的一部分。

这些要求是精心构建的,从各种操作的算法复杂性到迭代器失效规则。实施容器没有任何好处,任何人都无法依赖该实现的优势。

C ++委员会是否可以在未来的标准中引入一个具有不同名称和不同约束的新容器,供应商可以按照您的描述实施?是的,他们可以。

答案 1 :(得分:2)

你的问题是你缺少那个容器。从这样的事情开始:

template<typename T>
class bi_vec {
  std::unique_ptr<char[]> raw;
  std::size_t first = 0;
  std::size_t last = 0;
  std::size_t capacity = 0;
  char* raw_get( std::size_t index ) {
    return &raw[index*sizeof(T)];
  }
  char const* raw_get( std::size_t index ) const {
    return &raw[index*sizeof(T)];
  }
  T& get( std::size_t index ) {
    return *reinterpret_cast<T*>(raw_get(index));
  }
  T const& get( std::size_t index ) const {
    return *reinterpret_cast<T const *>(raw_get(index));
  }
  char* raw_before() {
    if (first < 1)
      grow();
    --first;
    return raw_get(first);
  }
  char* raw_after() {
    if (last+1 >= capacity)
      grow();
    ++last;
    return raw_get(last-1);
  }
  void grow() {
    std::vector new_capacity = (capacity+1)*2;
    std::size_t new_first = (new_capacity - (last-first)) / 2;
    std::size_t new_last = new_capacity - new_first;
    std::unique_ptr<char[]> new_buff( new char[new_capacity*sizeof(T)] );
    T* b = &get(0);
    T* e = &get(last-first);
    std::move( b, e, reinterpret_cast<T*>( &new_buff[new_first*sizeof(T)] ) );
    std::swap( buff, raw );
    std::swap( new_capacity, capacity );
    std::swap( new_first, first );
    std::swap( new_last, last );
    for (T* it = b; it != e; ++it) {
      it->~T();
    }
  }
public:
  T& operator[]( std::size_t index ) { return get(index); }
  T const& operator[]( std::size_t index ) const { return get(index); }
  template<class... Us>
  void emplace_back( Us&&...us ) {
    char* a = raw_after();
    new (a) T( std::forward<Us>(us) );
  }
  template<class... Us>
  void emplace_front( Us&&...us ) {
    char* a = raw_before();
    new (a) T( std::forward<Us>(us) );
  }
  ~bi_vec() {
    for( std::size_t i = 0; i != last-first; ++i ) {
      get(i).~T();
    }
  }
};

并添加迭代器(我很想使用boost迭代器助手或启动的原始指针)以及您需要的任何接口。请注意,上述需求可以确保它保持异常安全。