C++ Logo

std-proposals

Advanced search

Re: [std-proposals] CTAD for std::inplace_vector?

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Sun, 28 Jul 2024 17:20:21 -0400
On Sun, Jul 28, 2024 at 10:20 AM Hewill Kang via Std-Proposals <
std-proposals_at_[hidden]> wrote:

>
> Quoting the original paper’s description
> <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p0843r14.html#Deduction-guides>
> for deduction guides for std::inplace_vector: "*Unlike the other
> containers, inplace_vector** does not have any deduction guides because
> there is no case in which it would be possible to deduce the second
> template argument, the capacity, from the initializer.*"
>
> However, I found that in some cases it is possible to enable CTAD for
> std::inplace_vector, for example when we accept a std::array or std::span:
>
> template<class T, size_t N>
> inplace_vector(from_range_t, const std::array<T, N>&) ->
> inplace_vector<T, N>; // new CTAD
> Then we can:
>
> std::inplace_vector v{std::from_range, std::array{1, 2, 3}};
>

The array has size 3 *and capacity 3*, whereas the inplace_vector has size
3 *and capacity unknown*.
You're asking why the (initial) size of an inplace_vector might ever be
different from its capacity... but that's literally the reason that
inplace_vector exists! If you want a container whose size is always equal
to its capacity, you could just use `array` directly.

I think this makes meta-programming more user-friendly and convenient, for
> example, converting index_sequence into inplace_vector and then applying
> the algorithm in <algorithm>:
>
> constexpr inplace_vector v = []<size_t... Is>(index_sequence<Is...>) {
> inplace_vector v{from_range, array{Is...}};
> auto [begin, end] = ranges::remove(v, 42);
> v.erase(begin, end);
> return v;
> }(std::make_index_sequence<N>{});
> Would it be valuable for inplace_vector to introduce std::array or
> std::span-specific CTADs?
>

IMO, no it would not.

Notice that what you have there is equivalent in effect to:

constexpr auto v = []<size_t... Is>(index_sequence<Is...>) {
  size_t v[] = {Is...};
  auto [begin, end] = std::ranges::remove(v, 42);
  return std::inplace_vector<size_t, *sizeof...(Is)*>(v, begin);
}(std::make_index_sequence<N>{});

`*sizeof...(Is)*` is almost certainly the *wrong* capacity, there. Either
you're planning to add more elements to it (so you need more capacity), or
you're not (in which case you should use a capacity equal to the *final*
size, not the size *before* the remove).

I also have a vague sense that since inplace_vector is expensive to
move-construct, we really don't want to encourage people to create
inplace_vectors without thinking about capacity — and then have to move
them into the "proper" type later on. We can get away with that for types
like `shared_ptr<U>` and `unique_ptr<U>`, where converting to the correct
type is O(1). But for `inplace_vector<T, M>`, converting to
`inplace_vector<T, N>` is an O(M) operation.

HTH,
Arthur

Received on 2024-07-28 21:20:35