What is a “span” and when should I use one?

前端 未结 2 644
被撕碎了的回忆
被撕碎了的回忆 2020-11-22 06:19

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 som

2条回答
  •  终归单人心
    2020-11-22 06:55

    What is it?

    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 length; } 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 it:

    • 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.
    • 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 (respectively, span) instead of a free-standing T* (respectively const T*) for which you have the length value. 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+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);

      ... but with absolutely none of the overhead most container classes incur.

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

    Is it really not in the standard library (as of C++17)?

    std::span has actually been added to the standard library - but only as of C++20. 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 how do I use 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 [11]. 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 going into the standard libraryin 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).

提交回复
热议问题