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