span
's array
constructor is too strictSection: 23.7.2.2.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(*)[]
?
std::span
from a std::array<const T, X>
given the current
set of constructors of std::span
(23.7.2.2.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
(23.7.2.2.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
.
Container
(23.7.2.2.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:
std::span
handles C-arrays and std::arrays
in an asymmetric way. The constructor
taking a C-array (23.7.2.2.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.
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.
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.
Modify 23.7.2.2.1 [span.overview], class template
span
synopsis, as indicated:template<class ElementType, size_t Extent = dynamic_extent> class span { public: […] // 23.7.2.2.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; […] };Modify 23.7.2.2.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:
(11.1) —
extent == dynamic_extent || N == extent
istrue
, and(11.2) —
remove_pointer_t<decltype(data(arr))>(*)[]
is convertible toElementType(*)[]
.-12- Effects: Constructs a
-13- Ensures:span
that is a view over the supplied array.size() == N && data() == data(arr)
istrue
.
[2020-02 Status to Immediate on Thursday night in Prague.]
Proposed resolution:
This wording is relative to N4849.
Modify 23.7.2.2.1 [span.overview], class template span
synopsis, as indicated:
template<class ElementType, size_t Extent = dynamic_extent> class span { public: […] // 23.7.2.2.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; […] };
Modify 23.7.2.2.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:
(11.1) —
extent == dynamic_extent || N == extent
istrue
, and(11.2) —
remove_pointer_t<decltype(data(arr))>(*)[]
is convertible toElementType(*)[]
.-12- Effects: Constructs a
-13- Postconditions:span
that is a view over the supplied array.size() == N && data() == data(arr)
istrue
.