使用ranges-v3创建自定义视图:无法与transform管道连接问题排查
自定义ranges-v3视图无法接入transform管道的问题
我希望基于ranges-v3库实现一个自定义view,要求它能顺畅接入ranges::views::transform(...)的链式调用管道,但目前的实现无法满足这个需求。
场景说明
我先写了一个用于解引用输入并完美转发结果的函数:
template < typename T > decltype(auto) deref(T&& t) { return std::forward< T >(t); } template < typename T > requires std::is_pointer_v< std::remove_cvref_t< T > > or /* 其他类似智能指针的类型判断 */ decltype(auto) deref(T&& t) { return *std::forward< T >(t); }
接着是封装该函数的自定义视图实现:
template < ranges::range Range > class deref_view: public ranges::view_base { public: struct iterator; deref_view() = default; deref_view(ranges::range auto&& base) : m_base(base) {} iterator begin() { return ranges::begin(m_base); } iterator end() { return ranges::end(m_base); } private: Range m_base; }; template < ranges::range Range > struct deref_view< Range >::iterator: ranges::iterator_t< Range > { using base = ranges::iterator_t< Range >; iterator() = default; iterator(const base& b) : base{b} {} iterator operator++(int) { return static_cast< base& >(*this)++; } iterator& operator++() { ++static_cast< base& >(*this); return (*this); } decltype(auto) operator*() const { return deref(*static_cast< base >(*this)); } }; template < ranges::range Range > deref_view(Range&&) -> deref_view< ranges::cpp20::views::all_t< Range > >; struct deref_fn { template < typename Rng > auto operator()(Rng&& rng) const { return deref_view{ranges::views::all(std::forward< Rng >(rng))}; } template < typename Rng > friend auto operator|(Rng&& rng, deref_fn const&) { return deref_view{ranges::views::all(std::forward< Rng >(rng))}; } }; namespace ranges::views { constexpr deref_fn deref{}; }
测试情况
当我把这个视图用于存储int类型shared_ptr的vector时,功能正常:
std::vector<std::shared_ptr<int> > vec{ std::make_unique<int>(0), std::make_unique<int>(1), std::make_unique<int>(2) }; for (auto elem : vec | ranges::views::deref) { std::cout << elem << std::endl; }
输出结果:
0 1 2
但将它接入transform视图的链式管道时,编译失败:
for (auto elem : vec | ranges::views::deref | ranges::views::transform([](const auto& in) { return in; })) { std::cout << elem << std::endl; }
问题分析与解决方案
你的自定义视图未满足ranges-v3对视图的完整概念要求,导致后续transform无法完成类型推导,核心问题如下:
- 自定义迭代器未暴露必要的关联类型(如
value_type、reference等),ranges管道依赖这些类型完成链式调用的适配 deref_view未完整实现view概念所需的嵌套类型定义
修复方案
方案一:复用transform_view简化实现
无需手动编写视图和迭代器,直接基于ranges-v3的transform_view封装deref逻辑,天然满足所有概念要求:
struct deref_fn { template <ranges::input_range Rng> constexpr auto operator()(Rng&& rng) const { return ranges::views::transform(std::forward<Rng>(rng), [](auto&& elem) -> decltype(auto) { return deref(std::forward<decltype(elem)>(elem)); }); } template <ranges::input_range Rng> friend constexpr auto operator|(Rng&& rng, const deref_fn&) { return operator()(std::forward<Rng>(rng)); } }; namespace ranges::views { constexpr deref_fn deref{}; }
方案二:补全自定义视图的概念要求
如果坚持手动实现,需要补全迭代器和视图的关联类型:
template <ranges::range Range> class deref_view : public ranges::view_base { public: struct iterator; deref_view() = default; explicit deref_view(Range base) : m_base(std::move(base)) {} iterator begin() { return iterator(ranges::begin(m_base)); } auto end() { return ranges::end(m_base); } // 暴露range关联类型 using value_type = std::remove_cvref_t<decltype(deref(*ranges::begin(std::declval<Range>())))>; using reference = decltype(deref(*ranges::begin(std::declval<Range>()))); using iterator_category = typename ranges::iterator_t<Range>::iterator_category; private: Range m_base; }; template <ranges::range Range> struct deref_view<Range>::iterator : ranges::iterator_t<Range> { using base = ranges::iterator_t<Range>; // 补全迭代器关联类型 using value_type = typename deref_view::value_type; using reference = typename deref_view::reference; using pointer = std::add_pointer_t<reference>; using difference_type = typename base::difference_type; using iterator_category = typename base::iterator_category; iterator() = default; explicit iterator(base it) : base(std::move(it)) {} iterator operator++(int) { auto tmp = *this; ++static_cast<base&>(*this); return tmp; } iterator& operator++() { ++static_cast<base&>(*this); return *this; } decltype(auto) operator*() const { return deref(*static_cast<const base&>(*this)); } pointer operator->() const { return std::addressof(**this); } }; // 推导指引保持不变 template <ranges::range Range> deref_view(Range&&) -> deref_view<ranges::views::all_t<Range>>; // deref_fn和命名空间定义保持不变
内容的提问来源于stack exchange,提问作者Michael A
相关产品推荐
相关产品推荐

