The Guidelines Support Library is a Microsoft implementation of some of the types and functions described in the C++ Core Guidelines maintained by the Standard C++ Foundation. Among the types provided by the GSL is span<T> formerly known as array_view<T> . This article is an introduction to this type.

span<T> is a non-owning range of contiguous memory recommended to be used instead of pointers (and size counter) or standard containers (such as std::vector or std::array ).

Suppose you want to create a function that displays the content of a container. Such a function could look like this:

template <typename T> void display(std::vector<T> const & data) { for (auto const e : data) std::cout << e << ' '; std::cout << std::endl; } std::vector<int> v{ 1, 2, 3, 4, 5 }; display(v); 1 2 3 4 5 6 7 8 9 template < typename T > void display ( std :: vector < T > const & data ) { for ( auto const e : data ) std :: cout << e << ' ' ; std :: cout << std :: endl ; } std :: vector < int > v { 1 , 2 , 3 , 4 , 5 } ; display ( v ) ;

This will work with vectors, but not with arrays or lists. So then you need overloads in order to support other containers.

template <class Iterator> void display(Iterator const b, Iterator const e) { for (Iterator i = b; i != e; ++i) std::cout << *i << ' '; std::cout << std::endl; } template <typename T> void display(std::vector<T> const & data) { display(std::begin(data), std::end(data)); } template <typename T, size_t S> void display(std::array<T, S> const & data) { display(std::begin(data), std::end(data)); } std::vector<int> v{ 1, 2, 3, 4, 5 }; display(v); std::array<int, 5> a{1, 2, 3, 4, 5}; display(a); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 template < class Iterator > void display ( Iterator const b , Iterator const e ) { for ( Iterator i = b ; i != e ; ++ i ) std :: cout << * i << ' ' ; std :: cout << std :: endl ; } template < typename T > void display ( std :: vector < T > const & data ) { display ( std :: begin ( data ) , std :: end ( data ) ) ; } template < typename T , size _ t S > void display ( std :: array < T , S > const & data ) { display ( std :: begin ( data ) , std :: end ( data ) ) ; } std :: vector < int > v { 1 , 2 , 3 , 4 , 5 } ; display ( v ) ; std :: array < int , 5 > a { 1 , 2 , 3 , 4 , 5 } ; display ( a ) ;

But what if you now what to display the content of an int[] or an int* ?

The span<T> type is intended as a uniform interface over arrays, pointers and standard containers that can be used as a replacement of these types. It does not store a copy of the original data, only a pointer to data and counters.

template <typename T> void display(gsl::span<T> const & data) { for(auto const e : data) std::cout << e << ' '; std::cout << std::endl; } std::vector<int> v{ 1, 2, 3, 4, 5 }; gsl::span<int> s1{ v }; display(s1); std::array<int, 5> a{1, 2, 3, 4, 5}; gsl::span<int> s2{ a }; display(s2); int arr[] = { 1, 2, 3, 4, 5 }; gsl::span<int> s3{ arr }; display(s3); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 template < typename T > void display ( gsl :: span < T > const & data ) { for ( auto const e : data ) std :: cout << e << ' ' ; std :: cout << std :: endl ; } std :: vector < int > v { 1 , 2 , 3 , 4 , 5 } ; gsl :: span < int > s1 { v } ; display ( s1 ) ; std :: array < int , 5 > a { 1 , 2 , 3 , 4 , 5 } ; gsl :: span < int > s2 { a } ; display ( s2 ) ; int arr [ ] = { 1 , 2 , 3 , 4 , 5 } ; gsl :: span < int > s3 { arr } ; display ( s3 ) ;

The following helper functions are used in the samples below:

template <class Iterator> void show(Iterator b, Iterator e) { for (auto i = b; i != e; ++i) { std::cout << *i << ' '; } std::cout << std::endl; } template <class T> void show(gsl::span<T> const & s) { show(std::begin(s), std::end(s)); } template <class T, size_t R> void show(gsl::span<T, R> const & s) { show(std::begin(s), std::end(s)); } template <class T, size_t R, size_t C> void show(gsl::span<T, R, C> const & s) { show(std::begin(s), std::end(s)); } template <class T, size_t R, size_t C> void show(gsl::span<T, gsl::dynamic_range, R, C> const & s) { show(std::begin(s), std::end(s)); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 template < class Iterator > void show ( Iterator b , Iterator e ) { for ( auto i = b ; i != e ; ++ i ) { std :: cout << * i << ' ' ; } std :: cout << std :: endl ; } template < class T > void show ( gsl :: span < T > const & s ) { show ( std :: begin ( s ) , std :: end ( s ) ) ; } template < class T , size _ t R > void show ( gsl :: span < T , R > const & s ) { show ( std :: begin ( s ) , std :: end ( s ) ) ; } template < class T , size _ t R , size _ t C > void show ( gsl :: span < T , R , C > const & s ) { show ( std :: begin ( s ) , std :: end ( s ) ) ; } template < class T , size _ t R , size _ t C > void show ( gsl :: span < T , gsl :: dynamic_range , R , C > const & s ) { show ( std :: begin ( s ) , std :: end ( s ) ) ; }

Creating a span

A span can be created in many ways, including:

from a single value (variable, not a literal) int n = 1; gsl::span<int> s1 = n; // s1 = {1} gsl::span<int, 1> s2 = n; // se = {1} 1 2 3 int n = 1 ; gsl :: span < int > s1 = n ; // s1 = {1} gsl :: span < int , 1 > s2 = n ; // se = {1}

from a pointer and number of elements std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; gsl::span<int> s1(v.data(), 4); // s1 = {1, 2, 3, 4} gsl::span<int, 4> s2(v.data(), 4); // s2 = {1, 2, 3, 4} 1 2 3 std :: vector < int > v = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } ; gsl :: span < int > s1 ( v . data ( ) , 4 ) ; // s1 = {1, 2, 3, 4} gsl :: span < int , 4 > s2 ( v . data ( ) , 4 ) ; // s2 = {1, 2, 3, 4}

from a begin and end pointer std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; gsl::span<int> s1(&v[0], &v[4]); // s1 = {1, 2, 3, 4} gsl::span<int, 4> s2(&v[0], &v[4]); // s2 = {1, 2, 3, 4} 1 2 3 std :: vector < int > v = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } ; gsl :: span < int > s1 ( &v [ 0 ] , &v [ 4 ] ) ; // s1 = {1, 2, 3, 4} gsl :: span < int , 4 > s2 ( &v [ 0 ] , &v [ 4 ] ) ; // s2 = {1, 2, 3, 4}

from a C-like array int arr[] = { 1, 2, 3, 4, 5 }; gsl::span<int> s1{ arr }; // s1 = {1, 2, 3, 4, 5} gsl::span<int, 5> s2{ arr }; // s2 = {1, 2, 3, 4, 5} int arr2[2][3] = {1, 2, 3, 4, 5, 6}; gsl::span<int> s3{ arr2 }; // s3 = {1, 2, 3, 4, 5, 6} gsl::span<int, 6> s4{ arr2 }; // s3 = {1, 2, 3, 4, 5, 6} gsl::span<int, 2, 3> s5{ arr2 }; // s3 = {1, 2, 3, 4, 5, 6} 1 2 3 4 5 6 7 8 int arr [ ] = { 1 , 2 , 3 , 4 , 5 } ; gsl :: span < int > s1 { arr } ; // s1 = {1, 2, 3, 4, 5} gsl :: span < int , 5 > s2 { arr } ; // s2 = {1, 2, 3, 4, 5} int arr2 [ 2 ] [ 3 ] = { 1 , 2 , 3 , 4 , 5 , 6 } ; gsl :: span < int > s3 { arr2 } ; // s3 = {1, 2, 3, 4, 5, 6} gsl :: span < int , 6 > s4 { arr2 } ; // s3 = {1, 2, 3, 4, 5, 6} gsl :: span < int , 2 , 3 > s5 { arr2 } ; // s3 = {1, 2, 3, 4, 5, 6}

from a dynamic array int (*arr)[3][3] = new int[2][3][3]; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 3; j++) { for (int k = 0; k < 3; ++k) { arr[i][j][k] = k + j * 3 + i * 9; } } } gsl::span<int, gsl::dynamic_range, 3, 4> s1{ arr, 2 }; show(s1); // prints 0 1 2 3 4 .. 17 delete[] arr; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int ( * arr ) [ 3 ] [ 3 ] = new int [ 2 ] [ 3 ] [ 3 ] ; for ( int i = 0 ; i < 2 ; ++ i ) { for ( int j = 0 ; j < 3 ; j ++ ) { for ( int k = 0 ; k < 3 ; ++ k ) { arr [ i ] [ j ] [ k ] = k + j * 3 + i * 9 ; } } } gsl :: span < int , gsl :: dynamic_range , 3 , 4 > s1 { arr , 2 } ; show ( s1 ) ; // prints 0 1 2 3 4 .. 17 delete [ ] arr ;

from a standard container with contiguous memory layout such as array, vector or string std::array<int, 5> a = { 1, 2, 3, 4, 5 }; gsl::span<int> s1{ a }; // s1 = {1, 2, 3, 4, 5, 6} gsl::span<int, 5> s2{ a }; // s2 = {1, 2, 3, 4, 5, 6} std::vector<int> v = { 1, 2, 3, 4, 5 }; gsl::span<int> s3{ v }; // s3 = {1, 2, 3, 4, 5, 6} gsl::span<int, 5> s4{ v }; // s4 = {1, 2, 3, 4, 5, 6} std::string text = "sample"; gsl::span<const char> s5{ text }; // s5 = {'s', 'a', 'm', 'p', 'l', 'e'} 1 2 3 4 5 6 7 8 9 10 std :: array < int , 5 > a = { 1 , 2 , 3 , 4 , 5 } ; gsl :: span < int > s1 { a } ; // s1 = {1, 2, 3, 4, 5, 6} gsl :: span < int , 5 > s2 { a } ; // s2 = {1, 2, 3, 4, 5, 6} std :: vector < int > v = { 1 , 2 , 3 , 4 , 5 } ; gsl :: span < int > s3 { v } ; // s3 = {1, 2, 3, 4, 5, 6} gsl :: span < int , 5 > s4 { v } ; // s4 = {1, 2, 3, 4, 5, 6} std :: string text = "sample" ; gsl :: span < const char > s5 { text } ; // s5 = {'s', 'a', 'm', 'p', 'l', 'e'}

using the gsl::as_span() function: std::vector<int> v = { 1, 2, 3, 4, 5 }; auto sv = gsl::as_span(v); // sv = {1, 2, 3, 4, 5} std::array<int, 5> a = { 1, 2, 3, 4, 5 }; auto sa = gsl::as_span(a); // sa = {1, 2, 3, 4, 5} 1 2 3 4 5 std :: vector < int > v = { 1 , 2 , 3 , 4 , 5 } ; auto sv = gsl :: as_span ( v ) ; // sv = {1, 2, 3, 4, 5} std :: array < int , 5 > a = { 1 , 2 , 3 , 4 , 5 } ; auto sa = gsl :: as_span ( a ) ; // sa = {1, 2, 3, 4, 5}

Notice that it is not possible to create a span from an initializer_list because an initializer list is a temporary object and a span is a non-owning container, it does not make a copy of the data, and therefore it can end up containing dangling references to temporary data. For a detailed discussion on the topic see this issue.

Size of a span

A span can have zero, one or more dimensions, and each dimension can have a different size (number of elements). The number of dimensions is called rank and the number of elements in a dimension is called extent. You can retrieve the rank and extent using the functions with the same name.

gsl::span<int> s0{ nullptr }; // rank = 1, extent[0] = 0 gsl::span<int> s1; // rank = 1, extent[0] = 0 int arr[] = { 1, 2, 3, 4, 5 }; gsl::span<int> s2{ arr }; // rank = 1, extent[0] = 5 gsl::span<int, 5> s3{ arr }; // rank = 1, extent[0] = 5 std::cout << "rank=" << s2.rank() << std::endl; // rank=1 std::cout << "extent(0)=" << s2.extent(0) << std::endl; // extent[0]=5 std::cout << "extent<0>=" << s2.extent<0>() << std::endl; // extent[0]=5 gsl::span<int, 2, 3> s4{ arr, 6 }; // rank = 2, extent[0] = 2, extent[1] = 3 std::cout << "rank=" << s4.rank() << std::endl; // rank=2 std::cout << "extent(0)=" << s4.extent(0) << std::endl; // extent[0]=2 std::cout << "extent(1)=" << s4.extent(1) << std::endl; // extent[1]=3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 gsl :: span < int > s0 { nullptr } ; // rank = 1, extent[0] = 0 gsl :: span < int > s1 ; // rank = 1, extent[0] = 0 int arr [ ] = { 1 , 2 , 3 , 4 , 5 } ; gsl :: span < int > s2 { arr } ; // rank = 1, extent[0] = 5 gsl :: span < int , 5 > s3 { arr } ; // rank = 1, extent[0] = 5 std :: cout << "rank=" << s2 . rank ( ) << std :: endl ; // rank=1 std :: cout << "extent(0)=" << s2 . extent ( 0 ) << std :: endl ; // extent[0]=5 std :: cout << "extent<0>=" << s2 . extent < 0 > ( ) << std :: endl ; // extent[0]=5 gsl :: span < int , 2 , 3 > s4 { arr , 6 } ; // rank = 2, extent[0] = 2, extent[1] = 3 std :: cout << "rank=" << s4 . rank ( ) << std :: endl ; // rank=2 std :: cout << "extent(0)=" << s4 . extent ( 0 ) << std :: endl ; // extent[0]=2 std :: cout << "extent(1)=" << s4 . extent ( 1 ) << std :: endl ; // extent[1]=3

Subspans

It is possible to create subspans from a span . There are several functions that do that:

first() : returns the sub-span with the first N elements from the original span

: returns the sub-span with the first N elements from the original last() : returns the sub-span with the last N elements from the original span

: returns the sub-span with the last N elements from the original subspan() : returns the sub-span within the specified range (first and last positions) of the original span .

int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; gsl::span<int> s{ arr }; auto fs1 = s.first(0); // fs1 = {} auto fs2 = s.first(5); // fs2 = {1, 2, 3, 4, 5} auto fs3 = s.first<3>(); // fs3 = {1, 2, 3} auto ls1 = s.last(0); // ls1 = {} auto ls2 = s.last(5); // ls2 = {6, 7, 8, 9, 10} auto ls3 = s.last<3>(); // ls3 = {8, 9, 10} auto ns1 = s.subspan(0, 0); // ns1 = {} auto ns2 = s.subspan(0, 5); // ns2 = {1, 2, 3, 4, 5} auto ns3 = s.subspan(5, 0); // ns3 = {} auto ns4 = s.subspan<0, 0>(); // ns4 = {} auto ns5 = s.subspan<0, 5>(); // ns5 = {1, 2, 3, 4, 5} auto ns6 = s.subspan<5, 0>(); // ns6 = {} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int arr [ ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } ; gsl :: span < int > s { arr } ; auto fs1 = s . first ( 0 ) ; // fs1 = {} auto fs2 = s . first ( 5 ) ; // fs2 = {1, 2, 3, 4, 5} auto fs3 = s . first < 3 > ( ) ; // fs3 = {1, 2, 3} auto ls1 = s . last ( 0 ) ; // ls1 = {} auto ls2 = s . last ( 5 ) ; // ls2 = {6, 7, 8, 9, 10} auto ls3 = s . last < 3 > ( ) ; // ls3 = {8, 9, 10} auto ns1 = s . subspan ( 0 , 0 ) ; // ns1 = {} auto ns2 = s . subspan ( 0 , 5 ) ; // ns2 = {1, 2, 3, 4, 5} auto ns3 = s . subspan ( 5 , 0 ) ; // ns3 = {} auto ns4 = s . subspan < 0 , 0 > ( ) ; // ns4 = {} auto ns5 = s . subspan < 0 , 5 > ( ) ; // ns5 = {1, 2, 3, 4, 5} auto ns6 = s . subspan < 5 , 0 > ( ) ; // ns6 = {}

Comparisons

You can use the comparison operators (==, !=, , >=) with two spans. Equality is checked with std::equal (two ranges are equal if every element in the first range is equal to the element corresponding to the same position in the second range) and less/greater is checked with std::lexicographical_compare() (one range is less than another if the first mismatch element in the first range is less than the element on the same position in the second range).

int arr1[2][3] = { 1, 2, 3, 4, 5, 6 }; gsl::span<int, 6> s1{ arr1 }; int arr2[] = { 1, 2, 3, 4, 5, 6 }; gsl::span<int> s2{ arr2 }; int arr3[] = { 1, 2, 3 }; gsl::span<int> s3{ arr3 }; int arr4[] = { 3, 2, 1 }; gsl::span<int> s4{ arr4 }; std::cout << (s1 == s2) << std::endl; // prints 1 std::cout << (s2 == s3) << std::endl; // prints 0 std::cout << (s2 > s3) << std::endl; // prints 1 std::cout << (s4 != s3) << std::endl; // prints 1 std::cout << (s4 > s3) << std::endl; // prints 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int arr1 [ 2 ] [ 3 ] = { 1 , 2 , 3 , 4 , 5 , 6 } ; gsl :: span < int , 6 > s1 { arr1 } ; int arr2 [ ] = { 1 , 2 , 3 , 4 , 5 , 6 } ; gsl :: span < int > s2 { arr2 } ; int arr3 [ ] = { 1 , 2 , 3 } ; gsl :: span < int > s3 { arr3 } ; int arr4 [ ] = { 3 , 2 , 1 } ; gsl :: span < int > s4 { arr4 } ; std :: cout << ( s1 == s2 ) << std :: endl ; // prints 1 std :: cout << ( s2 == s3 ) << std :: endl ; // prints 0 std :: cout << ( s2 > s3 ) << std :: endl ; // prints 1 std :: cout << ( s4 != s3 ) << std :: endl ; // prints 1 std :: cout << ( s4 > s3 ) << std :: endl ; // prints 1

Element access

It is possible to access the content of a span either with iterators or indexes.

std::vector<int> v = { 1, 2, 3, 4, 5, 6 }; gsl::span<int> s{ v }; // prints 1 2 3 4 5 6 for (auto const & e : s) std::cout << e << std::endl; // prints 6 5 4 3 2 1 for (auto it = s.rbegin(); it != s.rend(); ++it) std::cout << *it << std::endl; // prints 1 2 3 4 5 6 for (auto it = std::begin(s); it != std::end(s); ++it) std::cout << *it << std::endl; 1 2 3 4 5 6 7 8 9 std :: vector < int > v = { 1 , 2 , 3 , 4 , 5 , 6 } ; gsl :: span < int > s { v } ; // prints 1 2 3 4 5 6 for ( auto const & e : s ) std :: cout << e << std :: endl ; // prints 6 5 4 3 2 1 for ( auto it = s . rbegin ( ) ; it != s . rend ( ) ; ++ it ) std :: cout << * it << std :: endl ; // prints 1 2 3 4 5 6 for ( auto it = std :: begin ( s ) ; it != std :: end ( s ) ; ++ it ) std :: cout << * it << std :: endl ;

When it comes to index access you can either index like a regular array (s[0], s[1][2], etc.) or using a special type called index .

std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; gsl::span<int> s1{ v }; gsl::span<int, 3, 4> s2{ v }; // prints 1 2 3 ... 12 for (int i = 0; i < s1.extent(0); ++i) std::cout << s1[i] << ' '; std::cout << std::endl; // prints // 1 2 3 4 // 5 6 7 8 // 9 10 11 12 for (int i = 0; i < s2.extent(0); ++i) { for (int j = 0; j < s2.extent(1); ++j) std::cout << s2[i][j] << ' '; std::cout << std::endl; } // uni-dimensional index gsl::index<1> i1 = { 0 }; std::cout << s1[i1] << std::endl; // prints 1 // bi-dimensional index gsl::index<2> i2 = { 1, 1 }; std::cout << s2[i2] << std::endl; // prints 6 // bi-dimensional index computed from another index gsl::index<2> i3 = i2 * 2; std::cout << s2[i3] << std::endl; // prints 11 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 std :: vector < int > v = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 } ; gsl :: span < int > s1 { v } ; gsl :: span < int , 3 , 4 > s2 { v } ; // prints 1 2 3 ... 12 for ( int i = 0 ; i < s1 . extent ( 0 ) ; ++ i ) std :: cout << s1 [ i ] << ' ' ; std :: cout << std :: endl ; // prints // 1 2 3 4 // 5 6 7 8 // 9 10 11 12 for ( int i = 0 ; i < s2 . extent ( 0 ) ; ++ i ) { for ( int j = 0 ; j < s2 . extent ( 1 ) ; ++ j ) std :: cout << s2 [ i ] [ j ] << ' ' ; std :: cout << std :: endl ; } // uni-dimensional index gsl :: index < 1 > i1 = { 0 } ; std :: cout << s1 [ i1 ] << std :: endl ; // prints 1 // bi-dimensional index gsl :: index < 2 > i2 = { 1 , 1 } ; std :: cout << s2 [ i2 ] << std :: endl ; // prints 6 // bi-dimensional index computed from another index gsl :: index < 2 > i3 = i2 * 2 ; std :: cout << s2 [ i3 ] << std :: endl ; // prints 11

Share this: Twitter

LinkedIn

StumbleUpon

Facebook

Reddit

More

Google

Email



Print

