简短的回答似乎是没有单一的标准容器可以完成这项工作,您必须自己编写。
事实证明这比我想象的要困难,因为指向生成对象的指针必须在内存管理器的生命周期内保持有效(在原始问题中通过对象不能移动或复制的请求实现)。这不包括使用std::vector::resize() 和std::vector::reserve()。 sequence_container<std::vector<T>> 设计仍然是可能的,但要么需要为每个新对象块创建另一个 vector,要么预先构建整个对象块,然后将它们分发出去,直到块用完。
为了避免这种情况,似乎必须编写一些chunk 类(用来代替vector)并处理allocator 问题。这是一个实现:
#include <list>
#include <memory>
template<typename type, typename allocator=std::allocator<type>,
template<typename,typename> class sequence_container = std::list>
class chunk_allocator
{
public:
using object = type;
using pointer = object*;
using size_type = std::size_t;
using alloc_traits = std::allocator_traits<allocator>;
private:
struct chunk
{
allocator alloc;
const pointer beg_data, end_capacity;
pointer end_data;
chunk(size_type cap, const allocator&all)
: alloc(all)
, beg_data(alloc_traits::allocate(alloc,cap))
, end_capacity(beg_data+cap)
, end_data(beg_data) {}
~chunk()
{
if(beg_data==nullptr) return;
for(; --end_data>=beg_data; --end_data)
alloc_traits::destroy(alloc,end_data);
alloc_traits::deallocate(alloc,beg_data,capacity());
}
size_type size() const noexcept { return end_data - beg_data; }
size_type capacity() const noexcept { return end_capacity - beg_data; }
pointer make(size_type n)
{
if(end_data + n > end_capacity)
return nullptr;
auto ptr = end_data;
for(; n; --n,++end_data)
alloc_traits::construct(alloc,end_data);
return ptr;
}
};
using chunk_alloc = typename alloc_traits::template rebind_alloc<chunk>;
using chunk_container = sequence_container<chunk,chunk_alloc>;
using chunk_iterator = typename chunk_container::iterator;
chunk_container chunks;
chunk_iterator last_chunk;
/// no default constructor
chunk_allocator() = delete;
/// no copy
chunk_allocator(chunk_allocator const&) = delete;
chunk_allocator&operator=(chunk_allocator const&) = delete;
public:
/// allow move
chunk_allocator(chunk_allocator&&) = default;
chunk_allocator&operator=(chunk_allocator&&) = default;
/// constructor
explicit
chunk_allocator(size_type initial_capacity, allocator const&alloc=allocator())
: chunks(alloc)
, last_chunk(chunks.emplace(chunks.end(),initial_capacity,alloc)) {}
/// invalid index
static constexpr size_type invalid = ~size_type(0);
/// find index for element, return invalid if not ours
size_type index(const object*ptr) const noexcept
{
size_type n=0;
for(auto c=chunks.begin(); c!=chunks.end(); ++c)
if(c->beg_data <= ptr && ptr < c->end_data)
return n + size_type(ptr-c->beg_data);
else
n += c->size();
return invalid;
}
/// obtain contiguous chunks of objects
/// \param[in] n \# objects in returned chunk
/// \param[in] chunk_size \# objects to allocate should we not have enough
/// \return pointer to first of n contiguous objects
object*create(const size_type n, size_type chunk_size=0)
{
if(n==0)
return nullptr;
if(last_chunk->end_data + n > last_chunk->end_capacity) {
if(chunk_size==0) chunk_size = last_chunk->capacity();
if(chunk_size< n) chunk_size = n;
last_chunk = chunks.emplace(chunks.end(),chunk_size,last_chunk->alloc);
}
return last_chunk->make(n);
}
};
// test
#include <iostream>
struct foo
{
int X;
static int C;
foo() : X(C++) { std::cout<<"foo::foo(): X="<<X<<std::endl; }
foo(foo const&) = delete;
foo&operator=(foo const&) = delete;
foo(foo &&) = delete;
foo&operator=(foo &&) = delete;
};
int foo::C=0;
int main()
{
std::cout<<" chunk_allocator<foo> C(3);"<<std::endl;
chunk_allocator<foo> C(3);
auto a = C.create(1);
std::cout<<" auto a=C.create(1)="<<a<<std::endl;
auto b = C.create(4);
std::cout<<" auto b=C.create(4)="<<b<<std::endl;
auto c = C.create(3);
std::cout<<" auto c=C.create(3)="<<c<<std::endl;
std::cout<<" a="<<a<<" a->X="<<a->X<<" index(a)="<<C.index(a)<<'\n'
<<" b="<<b<<" b->X="<<b->X<<" index(b)="<<C.index(b)<<'\n'
<<" c="<<c<<" c->X="<<c->X<<" index(c)="<<C.index(c)<<'\n';
}