28

There is a new std::inplace_vector in the C++ standard library that seems to have a fixed capacity defined at compile time. I'm trying to understand a use case for std::inplace_vector instead of std::array or std::vector. It seems like std::inplace_vector has the advantage of a fixed size capacity, like std::array, but instead does not require object initialization until insertion. But, unlike std::vector, std::inplace_vector has a fixed compile-time capacity.

Can anyone provide an example of where std::inplace_vector might be useful?

7
  • 4
    Can anyone provide an example of where std::inplace_vector might be useful? -- From the Notes section at the cppreference link: inplace_vector is useful in environments where dynamic memory allocations are undesired. Commented Oct 29, 2024 at 15:11
  • 5
    A side note: "c++ STL" should be "c++ standard library" (STL is not a synonym of it). Commented Oct 29, 2024 at 15:12
  • 1
    Only about a third of STL made it into the standard C++ library. The additions to the standard C++ library since then for containers and algorithms did not all come from STL, but rather from other projects such as Boost, Folly, Abseil, GSL, et cetera, or from individual research developers who contributed their technology as proposals. Commented Oct 29, 2024 at 15:27
  • 3
    @MarkRansom: (answering question in comment on deleted answer): while the standard doesn't directly state that inplace_vector won't use the heap, it does directly state: "Its capacity is fixed and its elements are stored within the inplace_vector object itself." This also means moves to/from them aren't normally "fast" or exception safe like they are with std::vector. Commented Oct 29, 2024 at 15:35
  • 1
    OT: I haven't heard about std::inplace_vector before, thanks for learning something new today :). Now, I just wonder whether we will ever have a combination of vector and inplace_vector in the standard library, something as Boost's small_vector or LLVM's SmallVector. Commented Oct 29, 2024 at 16:31

3 Answers 3

29

std::array requires the element type to be default constructible if you default construct the array

std::inplace_vector on the other hand does not since default construction creates no objects in the vector

This also means you can use a loop to initialize the members of the inplace_vector where you can't with a std::array.


Also, pointed out by @Quimby

inplace_vector<T,N> can store up to N elements while array<T,N> always stores N elements. For the latter one had to track the number of active elements separately. So the same reasoning for prefering std::span over pointer+length tuples can apply.

Sign up to request clarification or add additional context in comments.

2 Comments

Might be worth mentioning that the allocation-free nature of inplace vector can make it suitable for constexpr contexts where a plain vector wouldn't work (i.e. where the vector isn't deallocated in the same expression).
@TobySpeight: Although that utility is limited by this clause in the description: "If N > 0 and std::is_trivial_v<T> is false, then member functions of inplace_vector are not usable in constant expressions."
21

std::inplace_vector provides a resizable container, that avoids memory allocations entirely.

This type is useful as a fast local container, as long as:

  • you know what the upper limit of its size will be
    • (And you can afford using that much space without causing, say, a stack overflow)
  • you don't need an O(1) complexity move operation
    • (The elements can be moved individually, but the container itself cannot be moved)

Comments

5

std::inplace_vector:

A pointer to an element of an inplace_vector may be passed to any function that expects a pointer to an element of a C-array.

That part is similar as for (most) std::vectors (vector of bool can be different)

This can be pretty handy if you deal with a C-Library where you provide arrays of variable size which have an upper limit, the limit may be defined by the library, maybe defined by your use case.
I think something which looks and feels like a std::vector is much nicer to use than a plain old C-array with a separate size variable.

On a std::vector it is easy to forget to reserve() properly which may lead to invalid pointers or just worse performance.
With a std::inplace_vector the compiler forces you to think about the reserved memory and instead of hard to detect invalid pointers you will notice the error at the push_back() which tries to exceed the max size.
So in some cases it can prevent you from making mistakes.

The elements of std::inplace_vector are stored as direct member of the object and thus potentially on the stack.
Watch out with big sizes on local variables, the stack is typically much smaller than the heap.

Edit: I came across another good reason:
A std::vector (or anything else which requires dynamic allocation) is not usable within a constexpr expression.
The inplace storage of std::inplace_vector allows it to be used as a constexpr value.
You could do things like iterate over the container and calculate the sum of all elements at compile time (as long as the container and all contents are constexpr).
Sure, the same is possible with an std::array but for the array you need to know exactly how many elements you have and use index access, while std::inplace_vector supports a simple push_back

4 Comments

"(e.g. use resize() to ensure a certain length)" - Typically, you'd use reserve ahead of time to ensure a certain capacity, without actually constructing the objects. As long as you don't exceed that capacity or explicitly shrink the capacity, reserve gets you the same protections against pointer-invalidation. Unless that quote was giving an example of when invalidation could occur (a resize after taking the pointer), but that's frankly a pretty rare case next to just push_back/emplace_backing beyond current capacity. The clause is probably best dropped entirely.
@ShadowRanger oh, this was a bad typo, I actually meant reserve instead of resize. Yes, such situations are rare but a pain to identify if you have them. Only one push_back() too much and everything breaks. I will fix my answer (again)
“it is easy to forget” — I don't think it's a good argument. Yes, it is easy to forget to increment the for loop variable. It's easy to forget to set x to 42. It's easy to write code not up to spec. reserve() may be a premature optimisation, too, in which case it's better be "forgotten" until the profiler suggests otherwise. The question is where do you decide to stop optimising, i.e., a matter of opinion and preference.
@kkmmistrustsSE The forgetting and the performance are not the problem. The problem is that a simple forgetting can cause your C pointers to point to dead memory. Also the for loop can be used with a simpler syntax, where you have less possibilities to make mistakes and in the end will make less mistakes. Making less mistakes is a clear benefit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.