Today, I noticed this code:
for(std::size_t i{}; i != std::extent_v<arr>; ++i) { /* */ };
When arr is a built-in array type, this works fine. However, when arr is a std::array<T, N> or another class type that mimics std::array, the loop body will never execute.
If T is not an array type, or if it has a rank less than or equal to I, or if I is 0 and T has type 'array of unknown bound of U', then the result is 0.
For array-like types, size is usually equivalent to extent, such as std::dynamic_extent indicating that std::span has a dynamic size. However, using std::extent only produces the correct value for built-in arrays, which can confuse users or cause errors.
Some programming guidelines recommend using std::array instead of built-in arrays. During refactoring, this error might occur and be particularly insidious because users might not know that std::extent_v<std::array<T, N>> always results in 0. They might forget
about it or store this result in an unintuitive way.
Therefore, I propose that std::extent should use static_assert to reject any non-built-in array types.
I believe this change is safe: no correct code would become erroneous and still compile. While I'm not a metaprogramming expert and am unsure of the exact impact on metaprogramming, some code may depend on this behavior. However, reverting to the previous behavior
is inexpensive:
std::conditional<std::is_array_v<T>, std::extent<T>, std::integral_constant<std::size_t, 0>>
although it’s somewhat verbose.