如何通过索引访问range-v3过滤视图元素?
问题:如何通过索引访问range-v3过滤视图的元素?
上下文代码:
std::vector<int> v = {1, 2, 3, 4, 5}; auto v_view = v | ranges::views::filter([](int value) { return value & 1; });
尝试访问第二个元素时使用v_view.at(1),但编译失败,报错如下:
error: no matching member function for call to 'at' 20 | if (v_view.at(3) == 3) { | ~~~~~~~^~ /opt/compiler-explorer/libs/rangesv3/0.12.0/include/range/v3/view/interface.hpp:309:46: note: candidate template ignored: constraints not satisfied [with True = true] 309 | constexpr range_reference_t<D<True>> at(range_difference_t<D<True>> n) | ^ /opt/compiler-explorer/libs/rangesv3/0.12.0/include/range/v3/view/interface.hpp:308:31: note: because 'D<true>' (aka 'ranges::remove_if_view<ranges::ref_view<std::vector<int>>, ranges::logical_negate<(lambda at <source>:18:45)>>') does not satisfy 'random_access_range' 308 | requires True AND random_access_range<D<True>> AND sized_range<D<True>>)
原因是at()要求范围满足随机访问且时间复杂度为常数,而过滤视图属于非连续范围,无法满足该约束。目前找到的替代方案是:
*ranges::next(v_view.begin(), 1); /* do some stuff */
想问除了显式使用迭代器外,还有其他通过索引操作视图的方法吗?
可行方案
除了ranges::next,还有以下几种方式可以实现通过索引访问过滤视图元素:
1. 使用ranges::advance调整迭代器
和next逻辑类似,但可直接修改迭代器位置:
auto it = v_view.begin(); ranges::advance(it, 1); // 移动到第2个元素(索引1) *it; /* do some stuff */
2. 将过滤视图转换为随机访问容器
如果需要多次通过索引访问,建议先把过滤视图转换为std::vector这类随机访问容器,后续访问就能用[]或at(),代价是一次性遍历视图并存储元素:
std::vector<int> filtered_v(v_view.begin(), v_view.end()); filtered_v.at(1); /* do some stuff */
3. 自定义辅助函数实现线性索引访问
可以封装一个通用函数,对任意输入范围实现线性复杂度的索引访问,避免重复写迭代器操作:
template <ranges::input_range R> requires ranges::dereferenceable<ranges::iterator_t<R>> constexpr ranges::range_reference_t<R> nth_element(R&& r, ranges::range_difference_t<R> n) { auto it = ranges::begin(r); ranges::advance(it, n); return *it; } // 使用方式 nth_element(v_view, 1); /* do some stuff */
注意:以上非容器转换的方法,时间复杂度均为O(n),因为需要从起始位置遍历到目标索引对应的元素,这是过滤视图这类非连续范围的固有特性——无法直接计算目标元素的位置。
内容的提问来源于stack exchange,提问作者Kaiyakha
相关产品推荐
相关产品推荐

