HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppCritical

What is a "span" and when should I use one?

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
spanandshouldusewhenonewhat

Problem

Recently I've gotten suggestions to use span'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, and why (or when) is it a good idea to use it?

Solution

What is a "span"?

A span is:

  • A very lightweight abstraction of a contiguous sequence of values of type T somewhere in memory.



  • Basically a struct { T * ptr; std::size_t size; } with a bunch of convenience methods.



  • 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 spans:

  • Don't use a span in code that could just take any pair of start & end iterators (like std::sort, std::find_if, std::copy and other templated functions from `), and also not in code that takes an arbitrary range (see The C++20 ranges library for information about those). A span has stricter requirements than a pair of iterators or a range: element contiguity and presence of the elements in memory.



  • Don't use a span if you have a standard library container (or a Boost container etc.) which you know is just the right fit for your code. spans are not intended to supplant existing containers.



Now for when to actually use a span:

Use
span (respectively, span) instead of a free-standing T (respectively const T) when the allocated size also matters. So, replace functions like:

void read_into(int* buffer, size_t buffer_size);


with:

void read_into(span buffer);


Why should I use it? Why is it a good thing?

Oh, spans are awesome! Using a span...

-
means that you can work with that pointer+size / 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; and with less opportunities to exceed array bounds.

-
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.

-
is the reasonable alternative to passing
const vector& to functions when you expect your data to be contiguous in memory. No more getting scolded by high-and-mighty C++ gurus!

-
facilitates static analysis, so the compiler might be able to help you catch silly bugs.

-
allows for debug-compilation instrumentation for runtime bounds-checking (i.e.
span's methods will have some bounds-checking code within #ifndef NDEBUG ... #endif)

-
indicates that your code (that's using the span) doesn't own the pointed-to memory.

There's even more motivation for using
spans, 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:

  • Microsoft / Neil Macintosh's GSL contains a standalone implementation: gsl/span



  • GSL-Lite is a single-header implementation of the whole GSL (it's not that big, don't worry), including span.



The GSL implementation does generally assume a platform that implements C++14 support [12]. These alternative single-header implementations do not depend on GSL facilities:

  • martinmoene/span-lite requires C++98 or later



  • 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).

There is also a multi-dimensional extension of a span:
mdspan`; see this SO question.

Code Snippets

void read_into(int* buffer, size_t buffer_size);
void read_into(span<int> buffer);
int buffer[BUFFER_SIZE];
read_into(buffer, BUFFER_SIZE);
int buffer[BUFFER_SIZE];
read_into(buffer);

Context

Stack Overflow Q#45723819, score: 577

Revisions (0)

No revisions yet.