Replaces (Pointer, Length) Pairs
Replaces (Pointer, Length)

  • lightweight
  • non-owning view (= not responsible for allocating or deleting memory)
  • into contiguous memory block (of e.g., std::vector, C-array, …)
span<int> sequence of integers whose values can be changed
span<int const> sequence of integers whose values can't be changed
span<int,5> sequence of exactly 5 integers (number of values fixed at compile time)
void foo(span<int> s); void foo(int* a, int n);
  • well-expressed intent:
    foo accesses a sequence of ints and doesn't take ownership of memory
  • ambiguous semantics:
    does foo take ownership of the array (delete it afterwards)?
  • 1 parameter per data source ⇒ easy to call correctly
  • 2 parameters per data source ⇒ unnecessary confusion, especially when functions take multiple arrays
  • foo's implementation can use standard library conforming interface:
    empty, size, range-based for, begin, end, …
  • ugly and error-prone code inside foo that is specific to handling arrays: 2 parameters whose values can vary independently, nullptr-checks, …

#include <gsl/span>   +                    
#include <span>      
As Parameter
void print_ints  (span<int const> s);
void modify_ints (span<int> s);
Call With std:: Container:
std::vector<int> v {1,2,3,4};
print_ints( v );
Call With Stack-Array:
int a[100];  
print_ints( a );
Call With Poiner & Length:
auto n = get_number_of_elements();
auto p = get_pointer_to_memory();
print_ints( { p, n } );

span isolates memory/array handling from implementation of print_ints

Size & Data Access
span<> s = ;
if (s.empty()) return;
if (s.size() < 1024) {  }
// indexed access 
if (s[0] > 0) {  }
// spans in range-based for loops 
for (auto x : s) {  }
auto m1 = std::minmax(begin(s), end(s));
auto m2 = std::ranges::minmax(s);  
Comparing Spans
span<> s1 = ;
span<> s2 = ;
bool values_same = s1 == s2;
bool memory_same = s1.data() == s2.data();
Spans From Spans
span<> s = ;
auto first3elements = s.first(3);
auto last3elements  = s.last(3);
size_t offset = 2;
size_t count = 4;
auto ss = s.subspan(offset, count);
span<std::byte const> bs = s.as_bytes();
span<std::byte> wbs = s.as_writable_bytes();

Making Spans
Making Spans

In C++17 and C++20, span's template parameters can be deduced from the constructor arguments.

std::vector<int>  w {0, 1, 2, 3, 4, 5, 6};
std::array<int,4> a {0, 1, 2, 3};
 
std::span sw1 { w };                     
std::span sa1 { a };
 
gsl::span<int>   sw2 { w };                     
gsl::span<int,4> sa2 { a };
std::vector<int> w {0, 1, 2, 3, 4, 5, 6};
                          |----.------' 
std::span      s1 {w.data()+2, 5};                     
gsl::span<int> s2 {w.data()+2, 5};  
int a[100];  
std::span          s1 { a };                     
gsl::span<int>     s2 { a };                     
// constexpr size 
gsl::span<int,100> s2 { a };
auto len = get_length_at_runtime();
auto p = std::make_unique<int[]>(len);  
std::span      s1 {p.get(), len};                     
gsl::span<int> s2 {p.get(), len};  

Careful!

std::span s;
if (  ) {
  vector<int> w {1,2,3,4,5};
  s = std::span(w);
} // w destroyed!
cout << s[0]; // w's memory destroyed!
vector<int> w {1,2,3,4,5};
std::span s {w};
w.insert(w.begin(), {-1,-2,0});
cout << s[0]; // w might hold new memory