16

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?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
John M
  • 163
  • 5

1 Answers1

10

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 2
    Surprisingly the documentation does state it: _"These overloads only participate in overload resolution if extent == std::dynamic_extent || N == extent is true **and std::remove_pointer_t(*)[] is convertible to element_type (*)[]**"_ – Timo Aug 10 '19 at 18:12
  • @Timo Oh, weird. Yeah, we do have that wording... except the underlying type of the arrays isn't a template... – Barry Aug 10 '19 at 18:19
  • This is exactly what I found confusing, especially the specific choice of element_type for plain old arrays and value_type for arrays. Why would they be different? – John M Aug 10 '19 at 18:29