在STL实现中有中心分配队列或向量吗?

are there center-allocation deque or vector in STL implementations?

本文关键字:向量 队列 STL 实现 分配      更新时间:2023-10-16

我正在阅读关于deque s vs vector s的文章,并遇到了它的维基百科条目,其中说deque使用动态数组的三种可能实现之一是:

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

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

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

另外,在这样的容器中间插入/删除是否平均花费std::vector的一半时间?


更新:

正如@Lightness Races in Orbit所指出的那样,在当前的标准下不会使用这种实现,因为没有人可以从STL的每个合同中受益,每个人都将遭受其缺点。关于缺点,我还有一个问题:

是否有可能实现一个新的类似vectordeque的容器(比如bivector),这样除了std::vector的功能/操作符之外,

1)提供(平摊)常数时间push_front()pop_front()操作,

2)保证内存的连续性,但不能保证迭代器的有效性。

我想在后台有一个数组,deque上的许多间接/性能问题将会消失。

没有标准库(不是"STL")实现会为此烦恼,因为它有您提到的缺点,而优点不是std::deque要求的一部分。

这些需求是精心构建的,从各种操作的算法复杂性,到迭代器无效规则。以这样一种方式实现容器是没有好处的,没有人可以依赖该实现的优点。

c++委员会能否在未来的标准中引入一个新的容器,它有不同的名字和不同的约束,供应商可以像你描述的那样实现?是的,他们可以。

你的问题是你缺少这样的容器。像这样开始:

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迭代器帮助器,或原始指针来启动)以及您需要的任何接口。请注意,上面的代码需要做一些工作来确保异常安全。