This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.

### 3255. `span`'s `array` constructor is too strict

Section: 24.7.3.2 [span.cons] Status: C++20 Submitter: Jean Guegant & Barry Revzin Opened: 2019-08-10 Last modified: 2021-02-25

Priority: 2

View all other issues in [span.cons].

View all issues with C++20 status.

Discussion:

Barry Revzin:

From StackOverflow:

This compiles:

>     std::vector<int*> v = {nullptr, nullptr};
>     std::span<const int* const> s{v};

This does not:

>     std::array<int*, 2> a = {nullptr, nullptr};
>     std::span<const int* const> s{a};

The problem is that `span`'s constructors include

- A constructor template that takes any `Container` that is neither a raw array nor a `std::array`

- A constructor template that takes an `array<value_type, N>&`

- A constructor template that takes a `const array<value_type, N>&`

So the first is excluded, and the other two don't match. We can change the array constructor templates to take an `array<T, N>` with the requirement that `T(*)[]` is convertible to `ElementType(*)[]`?

Jean Guegant:

It is impossible to create a `std::span` from a `std::array<const T, X>` given the current set of constructors of `std::span` (24.7.3.2 [span.cons]):

>     std::array<const int, 4> a = {1, 2, 3, 4};
>     std::span<const int> s{a}; // No overload can be found.
>     std::span s{a}; // CTAD doesn't help either.

Both constructors accepting a `std::array` (24.7.3.2 [span.cons] p11) require the first template parameter of the `std::array` parameter to be `value_type`:

>     template<size_t N> constexpr span(array<value_type, N>& arr) noexcept;
>     template<size_t N> constexpr span(const array<value_type, N>& arr) noexcept;

`value_type` being defined as `remove_cv_t<ElementType>` — this constrains the first template parameter not to be `const`.

Both constructors accepting a generic `Container` (24.7.3.2 [span.cons] p14) have a constraint — (p14.3) `Container` is not a specialization of `array` — rejecting `std::array`.

While you can call `std::array<const T, X>::data` and `std::array<const T, X>::size` to manually create a `std::span`, we should, in my opinion, offer a proper overload for this scenario. Two reasons came to my mind:

1. `std::span` handles C-arrays and `std::arrays` in an asymmetric way. The constructor taking a C-array (24.7.3.2 [span.cons] p11) is using `element_type` and as such can work with `const T`:

   >     const int a[] = {1, 2, 3, 4};
   >     std::span<const int> s{a}; // It works

   If a user upgrades her/his code from C-arrays to a `std::array`s and literally take the type `const T` and use it as the first parameter of `std::array`, he/she will face an error.

2. Even if the user is aware that `const std::array<T, X>` is more idiomatic than `std::array<const T, X>`, the second form may appear in the context of template instantiation.

At the time this issue is written `gls::span`, from which `std::span` is partly based on, does not suffer from the same issue: Its constructor taking a generic `const Container&` does not constraint the `Container` not to be a `std::array` (although its constructor taking a generic `Container&` does). For the users willing to upgrade from `gsl::span` to `std::span`, this could be a breaking change.

[2019-09-01 Priority set to 2 based on reflector discussion]

[2020-02-13 Tim updates PR]

The previous PR's change to the raw array constructor is both 1) unnecessary and 2) incorrect; it prevents `span<const int>` from being initialized with an `int[42]` xvalue.

Previous resolution: [SUPERSEDED]

> This wording is relative to N4830.

> > The only change is to make the constructors templated on the element type of the array as well. We already have the right constraints in place. It's just that the 2nd constraint is trivially satisfied today by the raw array constructor and either always or never satisfied by the `std::array` one.

> 1. Modify 24.7.3.1 [span.overview], class template `span` synopsis, as indicated:

>    >     template<class ElementType, size_t Extent = dynamic_extent>
>    >     class span {
>    >     public:
>    >       […]
>    >       // 24.7.3.2 [span.cons], constructors, copy, and assignment
>    >       constexpr span() noexcept;
>    >       constexpr span(pointer ptr, index_type count);
>    >       constexpr span(pointer first, pointer last);
>    >       template<class T, size_t N>
>    >         constexpr span(Telement_type (&arr)[N]) noexcept;
>    >       template<class T, size_t N>
>    >         constexpr span(array<Tvalue_type, N>& arr) noexcept;
>    >       template<class T, size_t N>
>    >         constexpr span(const array<Tvalue_type, N>& arr) noexcept;
>    >       […]
>    >     };

> 2. Modify 24.7.3.2 [span.cons] as indicated:

>    >     template<class T, size_t N>
>    >       constexpr span(Telement_type (&arr)[N]) noexcept;
>    >     template<class T, size_t N>
>    >       constexpr span(array<Tvalue_type, N>& arr) noexcept;
>    >     template<class T, size_t N>
>    >       constexpr span(const array<Tvalue_type, N>& arr) noexcept;

>    > > -11- Constraints:

>    > > 1. (11.1) — `extent == dynamic_extent || N == extent` is `true`, and

>    > > 2. (11.2) — `remove_pointer_t<decltype(data(arr))>(*)[]` is convertible to `ElementType(*)[]`.

>    > > -12- Effects: Constructs a `span` that is a view over the supplied array.

>    > > -13- Ensures: `size() == N && data() == data(arr)` is `true`.

[2020-02 Status to Immediate on Thursday night in Prague.]

Proposed resolution:

This wording is relative to N4849.

1. Modify 24.7.3.1 [span.overview], class template `span` synopsis, as indicated:

   >     template<class ElementType, size_t Extent = dynamic_extent>
   >     class span {
   >     public:
   >       […]
   >       // 24.7.3.2 [span.cons], constructors, copy, and assignment
   >       constexpr span() noexcept;
   >       […]
   >       template<size_t N>
   >         constexpr span(element_type (&arr)[N]) noexcept;
   >       template<class T, size_t N>
   >         constexpr span(array<Tvalue_type, N>& arr) noexcept;
   >       template<class T, size_t N>
   >         constexpr span(const array<Tvalue_type, N>& arr) noexcept;
   >       […]
   >     };

2. Modify 24.7.3.2 [span.cons] as indicated:

   >     template<size_t N>
   >       constexpr span(element_type (&arr)[N]) noexcept;
   >     template<class T, size_t N>
   >       constexpr span(array<Tvalue_type, N>& arr) noexcept;
   >     template<class T, size_t N>
   >       constexpr span(const array<Tvalue_type, N>& arr) noexcept;

   > > -11- Constraints:

   > > 1. (11.1) — `extent == dynamic_extent || N == extent` is `true`, and

   > > 2. (11.2) — `remove_pointer_t<decltype(data(arr))>(*)[]` is convertible to `ElementType(*)[]`.

   > > -12- Effects: Constructs a `span` that is a view over the supplied array.

   > > -13- Postconditions: `size() == N && data() == data(arr)` is `true`.