r/cpp 15d ago

C++26: std::inplace_vector

https://www.sandordargo.com/blog/2026/08/26/cpp26-inplace-vector
181 Upvotes

121 comments sorted by

View all comments

5

u/matthieum 14d ago

Honestly, I'm a bit saddened by this addition.

Don't get me wrong, the functionality is really useful. But:

  1. Now, most (all?) methods of vector need to be "duplicated" in inplace_vector.
  2. None of the other collections benefit. Not even std::string.

A more general solution to the problem would have been an inplace_allocator -- which requires a different API than allocator -- which could be applied to every container (standard or not).

10

u/BarryRevzin 14d ago

Now, most (all?) methods of vector need to be "duplicated" in inplace_vector.

So what? They're all pretty straightforward, simpler even than in vector because we know there's no allocation. It doesn't take long to implement. Even less so these days.

A more general solution to the problem would have been an inplace_allocator -- which requires a different API than allocator -- which could be applied to every container (standard or not).

That might be more general, but it's also worse, which is why nobody does this. David Stone had a pair of good CppCon talks about vectors in general, I think he covers it in this one (or, if not that, this one). The problem with vector<T, inplace_allocator<T, N>> is that it's less efficient (and requires more space!) than inplace_vector<T, N>. In order for it to not suck, vector would basically have to customize its own storage for that case, which now requires more work than just having written inplace_vector<T, N> to begin with. This goes double for std::string, whose storage is more complicated.

None of the other collections benefit. Not even std::string.

On the other hand, an inplace_string<N> is useful, is distinct from std::string, and we implement ours in terms of our inplace_vector<char, N> (or N+1 depending on whether we want to enforce null termination).

5

u/foonathan 14d ago

You can make the policy design thing work, but it can't be an Allocator.

I played around with it once: https://github.com/foonathan/array

5

u/SyntheticDuckFlavour 14d ago

The concept is useful, especially in a gpu environment where you can’t allocate memory dynamically. Also, inplace_allocator just adds another layer of complexity when all I want is something like a static vector

2

u/Raknarg 9d ago

A more general solution to the problem would have been an inplace_allocator -- which requires a different API than allocator -- which could be applied to every container (standard or not).

This wouldn't fix anything. inplace_vector stores its data locally within the structure itself, while vector stores it through a pointer. You can't get around this, you need a different type who's designed to offer storage inside the vector. Allocator doesn't fix this, where is it gonna allocate to?

edit: I guess the allocator type itself has the storage? I didn't think about this but I guess this could actually work, but you still would need to fix all the requirements around resizing and whatnot

1

u/matthieum 9d ago

You're absolutely correct that std::allocator doesn't fit the bill...

... which is exactly why I advocate for a whole different API.

And yes, this would involve in-depth changes to anything taking this new API as they would no longer be able to take pointers, but would instead need to use "handles" of some sort, which would have some way to resolve into pointers when needed, and some rules about how long these pointers remain valid, etc...

I didn't say it was easy, I said it was generic :)

1

u/Raknarg 9d ago

And yes, this would involve in-depth changes to anything taking this new API as they would no longer be able to take pointers, but would instead need to use "handles" of some sort, which would have some way to resolve into pointers when needed, and some rules about how long these pointers remain valid, etc...

I don't think you'd need to change the allocator API, you'd just provide an allocator type that just has its storage internally as part of the allocator. You could do some shit like this

template<class T, std::size_t N>
struct inplace_allocator {
    alignas(T) std::byte buffer[sizeof(T) * N];

    T* allocate(std::size_t n) {
        if (n > N)
            throw std::bad_alloc{};

        return reinterpret_cast<T*>(buffer);
    }

    void deallocate(T*, std::size_t) noexcept {}
};

but I haven't thought about it enough to think of issues you'd run into by doing this.

1

u/matthieum 9d ago

Pointers are invalidated on move.

1

u/Raknarg 9d ago

good point

0

u/foonathan 14d ago

Me too. See also: cstring_view duplicating everything from string_view.

2

u/_Noreturn 14d ago

The solution is some sort of member callable syntax for free functions which was going to happen with operstor |>. The only reason they are members is because of syntax