The converting constructor of C++ std::span of the form:
template<class OtherElementType, size_t OtherExtent>
constexpr explicit(see below) span(
const span<OtherElementType, OtherExtent>& s) noexcept;
with constraints, can be used for the following purposes:
int arr[]{1, 2, 3};
span<int, 3> sf{arr};
// static span to dynamic span.
span<int> sd{sf};
// non-const to const span.
span<const int> scd{sd};
span<const int, 3> sfc{sf};
// dynamic to static span.
span<int, 3> sf2{sd};
What are some of the other uses this constructor which satisfy this constraint?
is_convertible_v<OtherElementType(*)[], element_type(*)[]> is true. [ Note: The intent is to allow only qualification conversions of the OtherElementType to element_type. — end note ]
The purpose is to allow implicitly converting between spans, as long as this conversion is a qualifying conversion and the sizes match. It can also be used for explicit conversions like in your exaples, but (8) is special because it's
explicitonly in a few cases. For example:This is possible, because there is a qualifying conversion from
inttoconst int, so we can convertstd::span<int>tostd::span<const int>. This use case is quite common, because makingconst std::span<T>doesn't ensure immutability of the elements, only astd::span<const T>does that.This qualifying conversion can actually be multiple levels deep, e.g. we can:
std::span<const int * const * const>as a function parameterstd::span<int**>to itHowever, that's not a unique property of this constructor, (7) does the same. You may ask: "Why don't we just use constructor (7)?"
This constructor works with any sized contiguous range, and a span is one. However, it is always explicit for a static extent, and only constructor (8) allows an implicit conversion between equally sized static spans. For example:
The examples that you've given all use explicit conversions, so they don't demonstrate what makes (8) special, namely that it's conditionally explicit in fewer situations than other constructors.