使用range-v3的concat和repeat导致内存访问错误的排查求助
我在库中使用range-v3和xtensor进行数据采样,原有代码运行正常。为简化mask_range的处理,尝试通过views::concat将其与views::repeat生成的无限nullopt序列合并后放入zip视图,却触发未定义行为:出现std::bad_alloc、内存访问错误,UndefinedBehaviorSanitizer检测到无效bool值加载。
尝试了两种实现方式:
- a)
views::concat(mask_range, views::repeat(std::nullopt)) - b)
views::concat(mask_range, views::repeat(std::optional<xarray<bool>>{std::nullopt}))
测试环境:clang-16、C++20模式,mask_range默认是长度为0的std::array<std::optional<xarray<bool>>, 0>。
需要解答:
- 为何选项a能编译?range-v3是否会将
std::nullopt_t转换为std::optional<xarray<bool>>? - 我的
views::concat/repeat用法是否有误,问题出在哪里?
1. 选项a能编译的原因
range-v3的views::repeat会自动推导视图的元素类型,以匹配拼接操作中另一个视图的元素类型。mask_range的元素是std::optional<xarray<bool>>,而std::optional支持从std::nullopt_t(即std::nullopt的类型)隐式构造,因此views::repeat(std::nullopt)会被推导为生成std::optional<xarray<bool>>类型的元素,最终满足views::concat对两个视图元素类型一致的要求,所以编译通过。
2. 用法错误与问题根源
核心问题在于使用无限长度视图与有限视图拼接后放入zip视图,会导致无限迭代行为:
views::repeat生成的是无限长度的视图,而zip视图的迭代逻辑是持续遍历,直到所有输入视图都耗尽。当其中一个视图无限时,zip不会自动停止,会持续尝试从无限视图取元素,同时可能越界访问其他有限视图的内容,进而触发内存访问错误、std::bad_alloc(极端场景下尝试分配无限内存),以及UndefinedBehaviorSanitizer检测到的无效值加载。- 当
mask_range是空数组时,拼接后的视图完全是无限的nullopt序列,zip会无限制迭代该序列,直接触发未定义行为。
正确做法是:让拼接后的视图长度与采样数据源视图的长度一致,使用views::repeat_n生成指定次数的nullopt(而非无限的views::repeat),保证zip视图能在有限次数内完成迭代。
内容的提问来源于stack exchange,提问作者Michael A

