Consider a large memory container. In this simple example an std::vector<int>
:
std::vector v = { 0, 1, 2, 3, 4, 5 };
std::span
allows me create a lightweight view over the memory. Now I want to simply print the span:
template<typename T>
void print(std::span<T> span) {
std::cout << '[';
if (span.size())
std::copy(span.begin(), span.end() - 1, std::ostream_iterator<int>(std::cout, ", "));
std::cout << span.back() << "]\n";
}
int main() {
std::vector v = { 0, 1, 2, 3, 4, 5 };
print(std::span{ v });
}
output:
[0, 1, 2, 3, 4, 5]
now I want to make subsets (which is where the std::span
actually becomes useful as a view). I can use iterators to specify my range and call this constructor(3) from std::span
template< class It, class End >
explicit(extent != std::dynamic_extent)
constexpr span( It first, End last );
But that doesn't work:
print(std::span{ v.begin() + 2, v.end() }); //error E0289
C++ no instance of constructor matches the argument list argument types are: (std::_Vector_iterator<std::_Vector_val<std::conditional_t<true, std::_Simple_types, std::_Vec_iter_types<int, size_t, ptrdiff_t, int *, const int *, int &, const int &>>>>, std::_Vector_iterator<std::_Vector_val<std::conditional_t<true, std::_Simple_types, std::_Vec_iter_types<int, size_t, ptrdiff_t, int *, const int *, int &, const int &>>>>)
there is the possibility of using the constructor(2) which takes a pointer and size:
print(std::span{ v.data() + 1, 3 }); //-> prints [1, 2, 3]
But that defeats the purpose of iterators.
How can I construct an std::span
using iterators? Am I missing something?
full code:
#include <iostream>
#include <vector>
#include <span>
#include <algorithm>
template<typename T>
void print(std::span<T> span) {
std::cout << '[';
if (span.size())
std::copy(span.begin(), span.end() - 1, std::ostream_iterator<int>(std::cout, ", "));
std::cout << span.back() << "]\n";
}
int main() {
std::vector v = { 0, 1, 2, 3, 4, 5 };
print(std::span{ v.begin() + 2, v.end() });
}
until MSVC has implemented the constructor I will be using this make_span
function:
template<typename It>
constexpr auto make_span(It begin, It end) {
return std::span<std::remove_pointer_t<It::pointer>>(&(*begin), std::distance(begin, end));
}
Using Visual Studio Community 2019 Version 16.7.5. Configuration: x64, Release. C++ Language Standard = /std:c++latest
A span provides a safe way to iterate over and index into objects that are arranged back-to-back in memory. Such as objects stored in a built-in array, std::array , or std::vector . If you typically access a sequence of back-to-back objects using a pointer and an index, a span is a safer, lightweight alternative.
gsl::span is a replacement for (pointer, length) pairs to refer to a sequence of contiguous objects. It can be thought of as a pointer to an array, but that knows its bounds. For example, a span<int,7> refers to a sequence of seven contiguous integers. A span does not own the elements it points to.
To compare the values that two iterators are pointing at, dereference the iterators first, and then use a comparison operator. Operator= -- Assign the iterator to a new position (typically the start or end of the container's elements).
The primary purpose of an iterator is to allow a user to process every element of a container while isolating the user from the internal structure of the container. This allows the container to store elements in any manner it wishes while allowing the user to treat it as if it were a simple sequence or list.
You can construct a span
using iterators, it has such a constructor (as added by P1394, which you can see in [views.span]):
template< class It, class End >
explicit(extent != std::dynamic_extent)
constexpr span( It first, End last );
It's just that MSVC's standard library doesn't implement it. The program compiles fine on gcc, as expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With