>  All our use-cases so far are use-cases for the allocating-but-not-constructing entrypoint.

Even on std::make_shared<std::string[]>(10) ? That is a case of allocating and constructing.

On Sun, Mar 12, 2023 at 3:43 PM Arthur O'Dwyer <arthur.j.odwyer@gmail.com> wrote:
On Sun, Mar 12, 2023 at 1:59 PM Breno Guimarães <brenorg@gmail.com> wrote:
>And now I'm confused: Is this make_contiguous_objects or make_contiguous_storage? Because most library users will need the latter, not the former.

It's "_objects". I don't agree most library users would need the latter. At least it's not in the use cases I looked at.

You give the following use-cases in the paper:

https://github.com/llvm/llvm-project/blob/2f887c9a760dfdffa584ce84361912fe122ad79f/libcxx/include/__memory/shared_ptr.h#L1139
(btw, you call this "the LLVM implementation," but it would be clearer to say "the libc++ implementation of make_shared")
requires allocation without construction (it needs to pass Args... to the ctor).

https://github.com/abseil/abseil-cpp/blob/d8933b836b1e1aac982b1dd42cc6ac1343a878d5/absl/container/internal/raw_hash_set.h#L1342
performs allocation without construction, although it probably doesn't care (its types being constructed are trivial).

https://github.com/mattreecebentley/plf_hive/blob/8c2bf6d9606df1d76900751ffffc472e994b529b/plf_hive.h#L174
requires allocation without construction (it doesn't want to construct `T` objects yet), although the current implementation wraps `T` in a union both for this reason and for other reasons, so default-constructing-the-union-type ends up doing the same thing as not-constructing, in the end.
 
Some might want to defer the initialization of some arrays, but then they use it like  `std::make_contiguous_objects<Metadata, ElementAlignedStorage>(nElements, nElements)`
And manually do part of the work.
But make_contiguous objects would give the buffer size calculation (including alignment) and the exception-safe creation of the metadata array.

Right, that's why the storage-allocating version is still a value-add over "nothing at all." It does the buffer size calculation, including alignment, and hands back the component-array pointers to the caller. (Ville, this answers your question about "why is this better than just using the allocator directly.")

But I don't see any use-cases yet where we want the T objects to be default-constructed. Default-constructing the T objects is explicitly impossible in some cases and harmless but unnecessarily slow in others. We haven't yet seen a case where it's helpful.  That doesn't mean you can't provide an additional entrypoint that does construct the Ts; it just means that we haven't yet seen a positive use-case for that entrypoint. All our use-cases so far are use-cases for the allocating-but-not-constructing entrypoint.

–Arthur