C++ Logo

std-proposals

Advanced search

[std-proposals] Risks of using std::extent on non-built-in array types

From: Yexuan Xiao <bizwen_at_[hidden]>
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.

Received on 2024-12-13 15:09:15