Why does C++ span's C style array constructor need type_identity_t?

144 views Asked by At

The C style array constructor for span is specified as follows

template<size_t N> constexpr span(
   type_identity_t<element_type> (&arr)[N]) noexcept;

Why is type_identity_t necessary? instead of just:

template<size_t N> constexpr span(
   element_type (&arr)[N]) noexcept;

As was originally defined in this proposal?

2

There are 2 answers

5
cbhattac On

It was because "span's deduction-guide for built-in arrays did not work" without that change. Please check this link for more details.

0
Yakk - Adam Nevraumont On

As cbhattac's answer explains, the problem was that span's deduction guide's picked the wrong overload.

In issue3369 a fix was developed.

The core problem was that:

template <size_t Size>
  requires (Extent == dynamic_extent || Extent == Size)
span(T (&)[Size]) {}

ctor generates an implicit deduction guide, and so does

template <typename T, size_t Extent>
span(T (&)[Extent]) -> span<T, Extent>;

The constructor builds a span with variable length, and the deduction guide builds one with a fixed length.

When passed an array of fixed length, the ideal deduced span should also be of fixed length. But it wasn't happening.

Naively, explicit deduction guilds beat ones produced from constructors, but that isn't true -- the constructor here is more constrained due to the requires (Extent == dynamic_extent || Extent == Size) clause. So it beats the deduction guide.

To fix this, type_identity_t<T> was used to block CTAD with this constructor completely. (An alternative that would also work was to add a trivial constraint to the deduction guide).