It occurs to me that one way to rescue this proposal is to follow the spirit of:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3586.pdf
1. Introduce a nested type vector<T, A>::node_ptr.
2. node_ptr is an alias for unique_ptr<T[], allocator_deleter<T, A>>.
3. allocator_deleter<T, A> looks something like:
template <class T, class Allocator>
class allocator_deleter
{
public:
using value_type = T;
using allocator_type = Allocator;
using traits = std::allocator_traits<allocator_type>;
using pointer = typename traits::pointer;
using size_type = typename traits::size_type;
private:
std::tuple<allocator_type, size_type, size_type> data_;
public:
allocator_deleter(allocator_type const& a, size_type size,
size_type capacity) noexcept
: data_(a, size, capacity)
{}
void operator()(pointer p) noexcept
{
for (pointer e = p + std::get<1>(data_); p != e;)
traits::destroy(std::get<0>(data_), std::addressof(*--e));
traits::deallocate(std::get<0>(data_), p, std::get<2>(data_));
}
allocator_type get_allocator() const noexcept {return std::get<0>(data_);}
size_type size() const noexcept {return std::get<1>(data_);}
size_type capacity() const noexcept {return std::get<2>(data_);}
static_assert((std::is_same<value_type, typename allocator_type::value_type>::value),
"Invalid allocator::value_type");
};
3a. If allocator_deleter needs to do anything fancier than this to handle details such as those Thiago alludes to, it is up to the author of vector to put those details into allocator_deleter.
3b. allocator_deleter is not necessarily a user-accessible name. Clients should reach for vector<T, A>::node_ptr::deleter_type if they need a name for this type.
4. Introduce to vector<T, A>:
template <class T, class A>
typename vector<T, A>::node_ptr
release();
5. Introduce an explicit vector<T, A> constructor:
template <class T, class A>
vector<T, A>::vector(node_ptr&& np);
Example use:
int
main()
{
std::vector<X> v;
for (int i = 0; i < 3; ++i)
v.push_back(X(i));
auto up = v.release();
for (auto i = 0; i < up.get_deleter().size(); ++i)
std::cout << up[i] << '\n';
}
Open questions (at least for me).
1. This was fun to create. But does it have sufficient use cases to justify standardization? Combined with a custom allocator built on malloc/free it *might* enable the memory ownership transfer with legacy code capability alluded to in N4359.
2. Is the API of allocator_deleter sufficiently general to serve the use cases it needs to? One possibility would be to add another pointer to its API to allow the first element to begin at a non-zero offset with respect to the allocated pointer. This *might* allow ownership transfer between vector and user-defined containers such as a “sliding buffer” (untested).
3. Is the error-proneness sufficiently reduced? I’m too biased to answer that question myself.
At any rate, I think this at least addresses the technical problems with the proposal. Please feel free to take this and run with it. At this point I’m not planning on proposing it.
Howard