Library / SDK
nonstd-lite/span-lite avatar
nonstd-lite/span-lite

span-lite gives you std::span semantics in a header for C++98

span lite - A C++20-like span for C++98, C++11 and later in a single-file header-only library

519 stars43 forksC++BSL-1.0

At a glance

What is it?
span-lite is a single-header, dependency-free implementation of a C++20-style span for C++98 and later, under the Boost Software License, with configuration macros that switch individual features on and off. It is aimed at code that has to keep compiling on old compilers, and the README is candid that it will always lag the standard.
Who is it for?
span-lite is a good fit for a codebase that supports compilers predating C++20 but wants a bounds-safe view over arrays, vectors and other contiguous sequences, since the whole library is one file you drop into an include directory. It is a poor fit for a project that compiles with C++20, because the library uses std::span when it is available, and a poor fit if you want a moving target-free library, since the standard version keeps gaining members.
Can I use it commercially?
Yes. BSL-1.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 13 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the library is for

span-lite is described as a single-file header-only library providing a bounds-safe view for sequences of objects, offering a C++20-like span for use with C++98 and later. The idea is a non-owning view over a contiguous range: you pass a C array, a std::array or a std::vector, and you get something that knows its size without owning the data. If std::span is available, the library uses it, unless configured otherwise. It also has a relationship with byte-lite, the same author's companion library: if byte-lite is detected, span-lite additionally provides as_bytes() and as_writable_bytes() for C++14 and earlier. The stated properties are ease of installation, meaning a single header, and freedom from dependencies other than the standard library, which is a small library's way of saying it will slot into a project that has strong constraints on what it pulls in.

The example, and what it demonstrates

The README opens with a small program that makes the point better than a description would:

cpp
#include "nonstd/span.hpp"
#include <array>
#include <vector>
#include <iostream>

std::ptrdiff_t size( nonstd::span<const int> spn )
{
    return spn.size();
}

int main()
{
    int arr[] = { 1, };

    std::cout << 
        "C-array:" << size( arr ) <<
        " array:"  << size( std::array <int, 2>{ 1, 2, } ) <<
        " vector:" << size( std::vector<int   >{ 1, 2, 3, } );
}

One function takes a span of const int and returns its size, and it is called with three different container types. The compile line in the README shows the whole thing building with a single invocation, and the output confirms the sizes:

bash
prompt> g++ -std=c++11 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe
C-array:1 array:2 vector:3

The include path is relative, which tells you the header is expected to sit in a directory you point at, not to be installed system-wide.

Installing it, which is copying a file

The installation instruction is one sentence: span-lite is a single-file header-only library, so you put span.hpp in the include folder directly into the project source tree, or somewhere reachable from your project. There is no configure step and no build artefact, which is the entire point. The repository is more elaborate than the library, though, with a CMakeLists.txt, a cmake/ directory, a conanfile.py, an example/ directory with numbered examples such as 01-basic.cpp and 02-span.cpp, a project/ directory, a script/ directory and a test/ directory. The package manager routes are also advertised, with a Conan badge pointing at the conan centre and a vcpkg port, and there are badges for trying it on wandbox and on godbolt online, which is the fastest way to see how a given compiler handles it before you commit to a version.

The configuration macros and what they switch

The README's structure is built around a table of features and the configuration section that follows it, and the pattern is consistent: each capability is on by default only where it matches the standard, and each non-standard addition is something you opt into. The features that are always there are the core of a span. The optional ones include back() and front() for element access, and the note about them is that they are later additions to the standard rather than part of the original design. There is a constructor from std::initializer_list, following proposal p2447, for compilers where a single-parameter constructor cannot be constrained to containers. There is a constructor taking an additional parameter of type with_container_t, used with the value with_container, for the same reason. There is construction from a std::array with const data. And there is a member function call operator() for element access, equivalent to operator[], which has been marked deprecated and exists mainly as a migration path for code written against an earlier version of this library.

The extension set, and the reason each one exists

The rest of the optional surface is a list of things the standard does not have. at() performs bounds-checked access, throwing std::out_of_range if the index is outside the span, unless exceptions have been disabled, in which case at(index_t) delegates the bounds checking to operator[](index_t). swap() exchanges two spans. Comparison functions operator==() and others compare the content of two spans, and the README is explicit that C++20's span will not provide comparison, that span-lite will omit comparison at default in the near future, and that this is part of a regular types discussion. same() determines whether two spans refer to identical data through the same type, and when enabled it is incorporated into operator==(). first(), last() and subspan() exist to avoid having to use the dot template syntax when the span is a dependent type. And make_span() creator functions compensate for the class template argument deduction that is missing from pre-C++17 compilers, which is the single most common reason people reach for this library.

Testing it and knowing which behaviour you get

The README suggests a way to find out what your configuration actually does. Depending on the compiler and the C++ standard in use, nonstd::span behaves less or more like std::span, and to get an idea of the capabilities of nonstd::span with your configuration you look at the output of the tests in test/span.t.cpp, issuing span-main.t --pass @. The repository also has a Building the tests section, and the top level shows a .editorconfig, a .gitattributes, a .tgitconfig and a .github directory, so the project is set up for the usual continuous integration treatment even though it is one header. The key point for an adopter is that the behaviour of the library is a function of your compiler and your chosen standard, which means the same header can give you two different APIs in two build configurations. The test output is where you find out which one you have.

Where span-lite is the wrong choice

Three cases. The first is a project that compiles with C++20, because the library uses std::span when it is available, and you get the standard behaviour with none of the extensions, so the library adds a dependency and a namespace for nothing. The second is a codebase with a strict no-legacy policy, since the whole premise is that C++98 works, and a project that has decided to raise its floor should raise it rather than carry a shim. The third is a team that wants the standard to keep improving without a maintenance burden, since the README is candid that the standard version will gain members and this one will not follow, so the gap widens over time. Against writing the small amount of pointer-and-length plumbing yourself, the difference in approach is that span-lite gives you a single type that works across container types, a bounds-safe accessor and the deduction helpers, at the cost of a header you vendor and keep.

Editorial conclusion

span-lite is a good fit for a codebase that supports compilers predating C++20 but wants a bounds-safe view over arrays, vectors and other contiguous sequences, since the whole library is one file you drop into an include directory. It is a poor fit for a project that compiles with C++20, because the library uses std::span when it is available, and a poor fit if you want a moving target-free library, since the standard version keeps gaining members. Check three things before you adopt it: that your oldest supported compiler is genuinely pre-C++20, because on a modern toolchain the library delegates to std::span and you get none of the extensions, that you read the configuration section, since the non-standard extensions such as back(), front(), at(), swap(), same() and the initializer_list constructor are each individually switchable, and that you keep the header in your own tree rather than relying on the package manager, since the documented installation is to put span.hpp into an include folder directly into the project source tree.

Frequently asked questions

What is span-lite?

It is a single-file header-only library providing a bounds-safe view for sequences of objects, offering a C++20-like span for C++98 and later. If std::span is available it is used instead, unless configured otherwise, and if byte-lite is detected the library also provides as_bytes() and as_writable_bytes() for C++14 and earlier.

How do I install span-lite?

You put span.hpp in the include folder directly into the project source tree, or somewhere reachable from your project. There is no build step, and the package manager alternatives are the Conan centre entry and a vcpkg port, both linked from the README.

Which C++ standards does span-lite support?

C++98 and later, and the README's example is compiled with -std=c++11. The library can provide make_span creator functions to compensate for the class template argument deduction that is missing from pre-C++17 compilers.

What optional features does span-lite provide beyond std::span?

Back and front member functions, construction from a std::initializer_list following p2447, a container constructor taking a with_container_t parameter, construction from a std::array with const data, a deprecated operator() for element access, at() with bounds checking that throws std::out_of_range unless exceptions are disabled, swap(), content comparison functions, same(), and first(), last() and subspan().

What licence is span-lite under?

The Boost Software License, with the text in LICENSE.txt at the repository root. The library has no dependencies other than the C++ standard library.

Official sources

  1. Issues
  2. License: BSL-1.0
  3. nonstd-lite/span-lite on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nonstd-lite-span-lite.svg)](https://hysenlabs.com/projects/nonstd-lite-span-lite)

Was this useful?