Thus std::array, std::span, std::string, std::string_view, std::vector, with hardned options turned on.
For the static thing, the right way in C++ is to use a template parameter,
template<typename T, int size>
int foo(T (&ary)[size]) {
return size;
}
-- https://godbolt.org/z/MhccKWocEIf you want to get fancy, you might make use of concepts, or constexpr to validate size at compile time.
In these applications, size and T are fixed -- you'd just take `std::span<uint8_t, XCHACHA20POLY1305_NONCE_SIZE>` rather than templating.
Other than that... I'm not sure what hobbling you have in mind. Many C23 features come directly from earlier C++ standards (auto, attribute syntax, nullptr, binary literals, true/false keywords). VLAs? Though these are optional in newer C standards, too.
[0]: https://en.cppreference.com/w/cpp/language/aggregate_initial...
Unfortunately C++20 designated init has been butchered so much compared to the C99 that it is pretty much useless in practice except for the most trivial structs (for instance designators must appear in order of declaration, array item init is completely missing, designator chaining doesn't work ... and those are only the most important things).