On Sun, Jul 28, 2024 at 10:20 AM Hewill Kang via Std-Proposals <std-proposals@lists.isocpp.org> wrote:

Quoting the original paper’s description 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