Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting std::span with dynamic extent to std::span with static extent

Tags:

c++

c++20

I used to work with gsl::span from the Guidelines Support Library for some time. With gsl::span, is was possible to define a function taking a static extent and calling it with a gsl::span of dynamic extent, e.g.

void f(gsl::span<int, 5> s) { }
...
std::vector<int> v(100);
f(gsl::make_span(v).subspan(42, 5));    // Works

After porting the code to std::span, I noticed that this is no longer possible:

void f(std::span<int, 5> s) { }
...
std::vector<int> v(100);
f(std::span(v).subspan(42, 5));    // Does not work

Is there any reason for this difference? Is there any recommended way to convert a std::span with dynamic extent to a std::span with fixed extent? Of course, it is possible to create a new span with fixed extent when calling the function:

f(std::span<int, 5>(&v[42], 5));

However, I think that the "subspan" variant is much better readable, it expresses the intention in a better way, and it allows appropriate range checking in a debug build.

like image 992
Holger Strauss Avatar asked Jul 23 '20 08:07

Holger Strauss


1 Answers

Is there any reason for this difference?

P1976 goes through this at length. Fixed-extent spans have explicit constructors on purpose to try to alleviate undefined behavior, so std::span<int> (the type of std::span(v).subspan(42, 5)) is not convertible to std::span<int, 5>.

Is there any recommended way to convert a std::span with dynamic extent to a std::span with fixed extent?

If both the offset and the count are constant expressions (which is true in this case), you can use the other overload of subspan():

f(std::span(v).subspan<42, 5>());

This overload exists the way it does so that you can take fixed-extent subspans of fixed-extent spans safely, but is less than ideally useful when wanting to take fixed-extent subspans of a dynamic-extent span, where ideally we'd just have wanted std::span(v).subspan<5>(42).

For that case, you can instead use first() in conjunction with subspan:

f(std::span(v).subspan(42).first<5>());

Slightly longer, but gets the job done.

Or you can go full out and use the constructor:

f(std::span<int, 5>(std::span(v).subspan(42, 5)));
f(std::span<int, 5>(v.data() + 42, 5));
like image 165
Barry Avatar answered Oct 13 '22 20:10

Barry