Date: Fri, 13 Dec 2024 15:09:06 +0000
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.
The C++ standard states<https://eel.is/c++draft/meta.unary.prop.query#1>:
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.
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.
The C++ standard states<https://eel.is/c++draft/meta.unary.prop.query#1>:
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.
Received on 2024-12-13 15:09:15