Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are span's array and std::array constructors different from its container constructors

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?

like image 288
John M Avatar asked Aug 10 '19 17:08

John M


People also ask

What is the difference between std :: array and array?

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.

What does std :: span do?

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.

Why do we span in C++?

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.

What is GSL span?

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.


1 Answers

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 is true, and
  • remove_­pointer_­t<decltype(data(arr))>(*)[] is convertible to ElementType(*)[].

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.

like image 178
Barry Avatar answered Oct 16 '22 07:10

Barry