Search code examples
c++iteratorc++20std-span

why can't I construct an std::span from iterators?


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


Solution

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