为何std::extents的index_type允许为有符号类型?如何确定其上下界?
在泛型函数中确定std::extents维度的索引上下界
首先明确核心前提:mdspan的索引模型是严格0起始的,不管std::extents的index_type是有符号还是无符号类型,合法索引的范围始终是从0到对应维度大小减一。这是C++标准对mdspan的定义,和index_type的符号属性无关。
针对泛型场景,你可以按以下方式处理索引上下界:
1. 确定下界
合法索引的下界固定为index_type{0}——因为mdspan不支持负数索引,即使index_type是有符号类型,负数索引本身就是非法的,无需考虑。
2. 确定上界
上界是对应维度大小减一,需要将size_type(即std::make_unsigned_t<index_type>)类型的结果转换为index_type:
- 先获取维度大小:
auto dim_size = ex.extent(dim); - 若
dim_size == 0,该维度无合法索引,直接跳过后续处理 - 否则,上界为
static_cast<index_type>(dim_size - 1)
标准保证合法的std::extents对象中,维度大小不会超出index_type能表示的非负范围,因此这个类型转换是安全的。
3. 索引合法性检查(更直接的方式)
如果不需要显式计算上下界,而是直接判断某个索引是否合法,可以直接对比:
template<class Extents> bool is_index_valid(const Extents& ex, std::size_t dim, typename Extents::index_type idx) { using size_type = typename Extents::size_type; const auto dim_size = ex.extent(dim); return idx >= typename Extents::index_type{0} && static_cast<size_type>(idx) < dim_size; }
这种方式无需处理空维度的特殊情况(当dim_size == 0时,static_cast<idx> < 0永远为假),代码更简洁安全。
4. 泛型遍历示例
以下是一个遍历指定维度合法索引的泛型函数示例:
template<class Extents> void iterate_dimension(const Extents& ex, std::size_t dim) { using index_type = typename Extents::index_type; using size_type = typename Extents::size_type; const size_type dim_size = ex.extent(dim); if (dim_size == 0) { // 空维度,无合法索引可遍历 return; } const index_type lower = index_type{0}; const index_type upper = static_cast<index_type>(dim_size - 1); for (index_type idx = lower; idx <= upper; ++idx) { // 处理当前索引idx // ... } }
为什么不用std::numeric_limits?
std::numeric_limits<index_type>::max()给出的是类型的最大取值范围,但mdspan的合法索引仅到维度大小减一,远小于类型的理论最大值。用它作为上界会包含大量无效索引,完全不符合实际需求。
内容的提问来源于stack exchange,提问作者Torsten Knodt
相关产品推荐
相关产品推荐

