1

The following works:

#include <vector>
#include <ranges>

int main() {
    auto view = std::vector<int>{0,1,2,3,4};
    auto s = std::span{view.begin(), view.end()};
    std::vector test(view.begin(), view.end());
}

But this does not:

#include <vector>
#include <ranges>

int main() {
    auto view = std::ranges::iota_view{0, 1000};
    auto s = std::span{view.begin(), view.end()};
    std::vector test(view.begin(), view.end());
}

The problem is, I have some generic code where I want to send it a range and then create a span over the range. I've tried sending in a vector and it is fine. The result from iota fails.

template <typename TRange>
requires std::ranges::random_access_range<TRange>
void Foo(TRange const & r)
{
     // The algorithm starts with a full span and then partitions
     auto s = std::span(r.begin(), r.end());
}

The code is being ported from boost, and there I would have used boost::make_iterator_range(), but my guess that this is superseded in the standard library by std::span. Is this right?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217

2 Answers2

6

You don't.

std::span<int> requires a contiguous range. std::vector<int> is a contiguous range, but views::iota(0, 100) is not contiguous, it's just random access.


Side-note: write views::iota(0, 1000), don't write ranges::iota_view{0, 1000}. There is almost never any reason to write ranges::meow_view over views::meow, and it can easily be worse - the latter doesn't always give you something whose type is the former. Think of meow_view as an implementation detail.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Barry
  • 286,269
  • 29
  • 621
  • 977
  • @bradgonesurfing Why do you need `subrange`? You're not even using it in your example. – Barry Jan 24 '23 at 20:12
  • I didn't include the whole example. Just stated that I needed a subrange. If it helps then I'm doing a recursive subdivision of a range algorithm and pushing subranges onto a stack. Douglas Peucker algorithm to be precise. – bradgonesurfing Jan 24 '23 at 20:23
  • 2
    @bradgonesurfing "If it helps then" ... Well, it would help if you asked the question you actually have. If the question is "how do I get the first or second half of an arbitrary random-access view?" then the answer still isn't `ranges::subrange`, it's probably `views::take` or `views::drop`. – Barry Jan 24 '23 at 20:45
  • It wasn't the question I had when I wrote the question. I was just translating code I had from boost::make_iterator_range and assumed (wrongly) that std::span was the right thing. But thankyou. views::take and views::drop look like a better choice for the algorithm and will optimize better. – bradgonesurfing Jan 24 '23 at 22:17
  • turns out that using ``take`` and ``drop`` is not ideal as I can't create a std::stack where T represents the type of the *subrange*. take of a take of a drop of a take builds a new type on each operation whereas doing the same thing with subrange it always returns the same type. – bradgonesurfing Jan 25 '23 at 09:11
  • https://godbolt.org/z/79c4nWTEs – bradgonesurfing Jan 25 '23 at 09:17
  • 1
    @bradgonesurfing I still have no idea what you're trying to do. And "take of a take of a drop of a take builds a new type on each operation" isn't necessarily true - calling the result of `v | take(0)` a `span` is pretty confusing because it's _not_ a `span`, but if you _made_ it one then `span1` would also be a `std::span`. And "subrange" only gives you the same type if you start from the same type of iterators, which you simply are by construction. – Barry Jan 25 '23 at 14:33
  • You don't need to know exactly what I'm doing. Suffice to say I have an input range that could be any random access range. It is subdivided and each subdivision is put into a stack. The subdivisions are removed from the stack and further subdivided. It has to be ensured that the operations to subdivide a subdivision always returns the same type. std::ranges::subrange does this. std::views::take and std::views::drop do not. – bradgonesurfing Jan 25 '23 at 15:33
  • If you are interested the algorithm is https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm But I'm not doing it recursively I'm using a stack to keep track of the subdivisions. The algorithm works fine and has been doing so for years. The only requirement was to change from boost::make_iterator_range to something equivalent. – bradgonesurfing Jan 25 '23 at 15:35
  • The actual implementation is https://gist.github.com/bradphelan/c14fbaa73e7fd3e18a9e3df40696ef2f – bradgonesurfing Jan 25 '23 at 15:41
1

The thing I'm looking for is not std::span but std::ranges::subrange, which is more generic, and works with non-contiguous memory.

#include <vector>
#include <ranges>

int main() {
    auto view = std::views::iota(0, 1000);
    auto s = std::ranges::subrange{view.begin(),view.end()};
    std::vector test(view.begin(), view.end());
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217