the purpose of this email is to float the idea on a standard library facility to make this an every(other)day technique rather than a trick.
For the sake of introducing some concreteness, let's start with this naive API:
template<class... Ts>
std::tuple<std::pair<Ts*, Ts*>...> make_contiguous_objects(initializer_for<Ts>...);
This function would return a tuple or begin/end pointers for each T in Ts that were created on the heap, given some information on how to initialize them.
So, for std::make_shared<T[]>(size_t n), a big part of the job could be achieved through:
auto objs = std::make_contiguous_objects<ControlBlock, T>(/*number of control blocks=*/ 1, /*number of Ts=*/ n);
make_shared<T> wants make_contiguous_objects<CtrlBlock, T>(1, 1).
make_shared<T[]> wants make_contiguous_objects<CtrlBlock, T>(1, n).
But in both cases, we need to preserve the ability to std::destroy only the T (when the last shared_ptr goes away but weak_ptrs are still being held), and then destroy the CtrlBlock and deallocate the whole shebang later (when the last weak_ptr goes away).
plf::hive<T>'s "allocate new capacity" function wants make_contiguous_objects<T, skipfield_type>(n, n).
But it doesn't want to construct the T objects yet; it just wants to allocate the memory for them. They'll be constructed (and destroyed) later, as the container's size() fluctuates.
In both plf::hive and std::allocate_shared, we have the same problem that we need to do uses-allocator construction for the objects. That's easily addressed (I think) by just doubling the size of your API:
template<class... Ts, class... Counts> std::tuple<Ts*...> make_contiguous_objects(Counts...);
template<class... Ts, class Allocator, class... Counts> auto allocate_contiguous_objects(const Allocator& a, Counts...);
This actually points out a second problem: Your API works great if the caller wants to default-construct their objects. But what if they want to construct them in some other way, like what if `T` is not default-constructible? Mind you, this is a lower-priority problem than "what if the caller doesn't want to construct them at all."
I think it won't be hard for you to implement your "first draft" API... but then you need to spend some time looking for use-cases (preferably in the STL or Boost or other well-known libraries that can be assumed not to be doing anything stupid by accident) and actually find out whether your API helps them. I suspect it will be almost good enough, but not quite, for one reason or another, in basically every real-world case.