I have been playing around with the latest specification for std::span
using the clang trunk and libc++ on Godbolt and find some of the constructors confusing.
In particular I find the constructors from a plain old array and and a std::array
to be different from other containers.
For example the following code appears to compile:
std::vector<int*> v = {nullptr, nullptr};
std::span<const int* const> s{v};
However this does not:
std::array<int*, 2> a = {nullptr, nullptr};
std::span<const int* const> s{a};
This seems to be in keeping with the way the constructors are described on cppreference.com, I am just struggling to understand why this is the case. Can anybody shed any light?
std::array is just a class version of the classic C array. That means its size is fixed at compile time and it will be allocated as a single chunk (e.g. taking space on the stack). The advantage it has is slightly better performance because there is no indirection between the object and the arrayed data.
std::span. The class template span describes an object that can refer to a contiguous sequence of objects with the first element of the sequence at position zero.
A span provides a safe way to iterate over and index into objects that are arranged back-to-back in memory. Such as objects stored in a built-in array, std::array , or std::vector . If you typically access a sequence of back-to-back objects using a pointer and an index, a span is a safer, lightweight alternative.
gsl::span is a replacement for (pointer, length) pairs to refer to a sequence of contiguous objects. It can be thought of as a pointer to an array, but that knows its bounds. For example, a span<int,7> refers to a sequence of seven contiguous integers. A span does not own the elements it points to.
This seems like an oversight. The array constructors are currently specified as:
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;
But should probably be specified as:
template<class T, size_t N>
requires std::convertible_to<T(*)[], ElementType(*)[]>
constexpr span(array<T, N>& arr) noexcept;
template<class T, size_t N>
requires std::convertible_to<const T(*)[], ElementType(*)[]>
constexpr span(const array<T, N>& arr) noexcept;
Which would make your example compile, as it is safe to do. I submitted an LWG issue. This is now LWG 3255.
The wording already has this constraint specified in [span.cons]/11:
template<size_t N> constexpr span(element_type (&arr)[N]) noexcept; 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;
Constraints:
extent == dynamic_extent || N == extent
istrue
, andremove_pointer_t<decltype(data(arr))>(*)[]
is convertible toElementType(*)[]
.
So we already have the right constraint. It's just that data(arr)
isn't actually dependent in any of these cases, so the constraint is trivially satisfied. We just need to make these templates.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With