You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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无法完成类型推导,核心问题如下:

  1. 自定义迭代器未暴露必要的关联类型(如value_type、reference等),ranges管道依赖这些类型完成链式调用的适配
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 09:25:01