I'm parsing a binary network data and i would like to make the process as allocation-less as possible. But now i realized there are 2 very similar concepts that are probably both good enough for my case, and those are std::basic_string_view<T>
and std::span<T>
.
So I was wondering. What are the differences between these 2 and are there any advantages using one over the other? One obvious difference is the availability, std::basic_string_view
is already in C++17 while std::span
is C++20 (but you can use the one in 'Guidelines Support Library' for older standards). But is there anything else? It should, because otherwise they wouldn't both make it into the standard.
The factual biggest difference is that std::basic_string_view
has a template parameter for traits (usually an instance of std::char_traits
) which is used to determine ordering, equality, and other characteristics concerning characters, which are needed for features such as std::basic_string_view<…>::find
, and the std::span
template has a template parameter for extent (which may be std::dynamic_extent
, in which case the std::span<T>
behaves more like a std::basic_string_view<T>
).
Other than that, there is little difference between a std::basic_string_view<T, Traits>
and a std::span<const T, std::dynamic_extent>
. They even convert into each other, but (assuming T
is not a const
type) a std::basic_string_view<T, Traits>
cannot be converted to a std::span<T, std::dynamic_extent>
. For any T
, the construction of a std::basic_string_view<T, Traits>
from a std::span<T, extent>
is possible but explicit; the construction of a std::span<T, extent>
from a std::basic_string_view<T, Traits>
is only explicit if extent
isn’t std::dynamic_extent
.
Of course, std::basic_string_view
and std::span
offer different features, but as I pointed out, that isn’t really an issue because when you have a std::span<T, extent>
in hand, you can always create a std::basic_string_view<T, Traits>
from it to use its features, and vice versa, just that the caveat with const
applies.
A whole different discussion is intention. Both, std::basic_string_view<T, Traits>
and std::span<T, extent>
are vocabulary types, and as similar as std::span<const T>
and std::basic_string_view<T>
might be, their intent differs. A std::span
represents binary data, a section of memory, something like that, whereas a std::basic_string_view
is supposed to be instantiated with a built-in character type, possibly with custom traits, or with a custom character type. This is evidenced by the fact that there are literal operators and an output operator template for std::basic_string_view
, but nothing like that for std::span
. On the other hand, std::span
has as_bytes
/ as_writable_bytes
which converts a std::span
into a std::span
of its underlying std::byte
s. That means, if your T
is not suitable for character-like printing, you should represent your memory sections with a std::span
.