std::span
在阅读 C++ Core Guidelines 的时候,可以发现,gsl::span
贯穿始终,下面是直接使用gsl::span
的一些guideline:
I.13: Do not pass an array as a single pointer
F.24: Use a span<T>
or a span_p<T>
to designate a half-open sequence
R.14: Avoid []
parameters, prefer span
ES.42: Keep use of pointers simple and straightforward
stackoverflow What is a “span” and when should I use one?
Recently I've gotten suggestions to use span<T>
's in my code, or have seen some answers here on the site which use span
's - supposedly some kind of container. But - I can't find anything like that in the C++17 standard library.
So what is this mysterious span<T>
, and why (or when) is it a good idea to use it if it's non-standard?
comments
1、std::span
was proposed in 2017. It applies to C++17 or C++20. Also see P0122R5, span: bounds-safe views for sequences of objects. Do you really want to target that language? It will be years before compilers catch up. – jww Jan 24 '18 at 0:29
A
What is it?
A span<T>
is:
1、A very lightweight abstraction of a contiguous sequence of values of type T
somewhere in memory.
2、Basically a struct { T * ptr; std::size_t length; }
with a bunch of convenience methods.
3、A non-owning type (i.e. a "reference-type" rather than a "value type"): It never allocates nor deallocates anything and does not keep smart pointers alive.
It was formerly known as an array_view
and even earlier as array_ref
.
When should I use it?
First, when not to use it:
1、Don't use it in code that could just take any pair of start &
end iterators, like std::sort
, std::find_if
, std::copy
and all of those super-generic templated functions.
NOTE: why?
2、Don't use it if you have a standard library container (or a Boost container etc.) which you know is the right fit for your code. It's not intended to supplant(替代) any of them.
Now for when to actually use it:
Use
span<T>
(respectively,span<const T>
) instead of a free-standingT*
(respectivelyconst T*
) when the allocated length or size also matter. So, replace functions like:void read_into(int* buffer, size_t buffer_size);
with:
void read_into(span<int> buffer);
Why should I use it? Why is it a good thing?
Oh, spans are awesome! Using a span
...
1、means that you can work with that pointer+length / start+end pointer combination like you would with a fancy, pimped-out standard library container, e.g.:
-
for (auto& x : my_span) { /* do stuff */ }
-
std::find_if(my_span.cbegin(), my_span.cend(), some_predicate);
-
std::ranges::find_if(my_span, some_predicate);
(in C++20)
... but with absolutely none of the overhead most container classes incur.
2、lets the compiler do more work for you sometimes. For example, this:
int buffer[BUFFER_SIZE];
read_into(buffer, BUFFER_SIZE);
becomes this:
int buffer[BUFFER_SIZE];
read_into(buffer);
... which will do what you would want it to do. See also Guideline P.5.
3、is the reasonable alternative to passing const vector<T>&
to functions when you expect your data to be contiguous in memory. No more getting scolded by high-and-mighty C++ gurus!
NOTE: 最后一句的意思是: 再也不会被那些高高在上的c++大师骂了!
4、facilitates static analysis, so the compiler might be able to help you catch silly bugs.
5、allows for debug-compilation instrumentation for runtime bounds-checking (i.e. span
's methods will have some bounds-checking code within #ifndef NDEBUG
... #endif
)
6、indicates that your code (that's using the span) doesn't own the pointed-to memory.
There's even more motivation for using span
s, which you could find in the C++ core guidelines - but you catch the drift.
But is it in the standard library?
edit: Yes, std::span
was added to C++ with the C++20 version of the language!
Why only in C++20? Well, While the idea is not new - its current form was conceived in conjunction with the C++ core guidelines project, which only started taking shape in 2015. So it took a while.
So how do I use it if I'm writing C++17 or earlier?
It's part of the Core Guidelines's Support Library (GSL). Implementations:
1、Microsoft / Neil Macintosh's GSL contains a standalone implementation: gsl/span
2、GSL-Lite is a single-header implementation of the whole GSL (it's not that big, don't worry), including span<T>
.
The GSL implementation does generally assume a platform that implements C++14 support [11]. These alternative single-header implementations do not depend on GSL facilities:
1、martinmoene/span-lite
requires C++98 or later
2、tcbrindle/span
requires C++11 or later
Note that these different span implementations have some differences in what methods/support functions they come with; and they may also differ somewhat from the version adopted into the standard library in C++20.
Further reading: You can find all the details and design considerations in the final official proposal before C++17, P0122R7: span: bounds-safe views for sequences of objects by Neal Macintosh and Stephan J. Lavavej. It's a bit long though. Also, in C++20, the span comparison semantics changed (following this short paper by Tony van Eerd).
A
A span<T>
is this:
template <typename T>
struct span
{
T * ptr_to_array; // pointer to a contiguous C-style array of data
// (which memory is NOT allocated or deallocated
// by the span)
std::size_t length; // number of elements in the array
// Plus a bunch of constructors and convenience accessor methods here
}
It is a light-weight wrapper around a C-style array, preferred by C++ developers whenever they are using C libraries and want to wrap them with a C++-style data container for "type safety" and "C++-ishness" and "feelgoodery". :)
Implementation
microsoft/GSL/include/gsl/span
martinmoene GSL-Lite
boost beast span
https://www.boost.org/doc/libs/master/boost/beast/core/span.hpp