On Thu, Mar 6, 2025 at 5:57 PM Zhihao Yuan <zy@miator.net> wrote:
On Thursday, March 6th, 2025 at 10:43 AM, Arthur O'Dwyer <arthur.j.odwyer@gmail.com> wrote:
On Tue, Mar 4, 2025 at 3:03 PM Zhihao Yuan <zy@miator.net> wrote:
If someone is designing their own container,
a simple rvalue reference to array is usually
enough

template<size_t N>
vector(T (&&arr)[N]);

// use
vector v1({2, 4});
vector v2({std::make_unique<int>(1), std::make_unique<int>(2)});


I would be skeptical of that, though.
(1) That disables the "normal" way of constructing from a bag of elements:
vector v1 = {2, 4}; // no longer works

We are used to it, but you already know the pitfalls.
vector v1 = {a.begin(), a.end()};

Well, that's an anti-pitfall in the case of std::vector: CTAD will see that you're giving it a bag of elements of type `decltype(a)::iterator`, and rightly put them into a `vector<decltype(a)::iterator>`.
Remember, curlies mean "sequence of elements of an aggregate, or something pretending to be an aggregate (such as a product type, container, or span)."
std::vector erroneously lacks `explicit` on its multi-argument ctors, which leads to the non-CTAD pitfall
    std::vector<int> v2 = {"1", "2"};  // boom, UB
but in that case using CTAD would actually eliminate the UB (by constructing a vector<const char*> instead).

The thing is, if you make your non-template VectorOfInt such that
    VectorOfInt v = {1, 2};
doesn't compile, then it's going to suck. I don't think you can get away with that level of user-hostility in container design.

(2) That disables P2752 static-storage optimization:
vector v1({
#embed "large-file.dat" // compiles, but blows your stack at runtime
});

[...] In fact I recall you had opinions of some kind about applying P2752 to array literals too, which I shot down as scope creep; do you think it's possible to fix the codegen in #2 with a core-language change? The problem I see is that you're explicitly wanting to move-out-of the elements of `arr`, so it can't possibly go into static storage, right?
Yes and no. If a class knew the input is not meant
for modification, a matrix for example, then it can
take T const (&&arr)[N], in which case the
semantics is identical to initializer_list, i.e., binding
a unique reference to a backing array and implying
that the data is immutable. And then a generic
container can choose whether to add const
programmatically, if that's what they want

https://godbolt.org/z/K84GzP8Kr

But that still blows your stack ( https://godbolt.org/z/Yo7nMPsav ) because temporary arrays don't get the two things you just said:—
- There is no "backing array" (no P2752 treatment — again, I'll take the blame for this, but it's a fact); there's just a huge temporary on the stack.
- IIUC there is no "implying that the data is immutable," because can't the callee still const_cast the pointer? Or are the elements of temporary arrays actually const objects, such that it's UB to modify them? I'm like 70% sure they're not actually const objects.

–Arthur