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

自定义deref视图适配vector但无法适配array/initializer_list问题排查

问题:自定义deref视图无法适配std::array与std::initializer_list

我基于ranges-v3库实现了一个deref视图,用于调用元素的.get()方法解引用std::reference_wrapper。该视图处理std::vector时表现正常,但无法应用于std::array或std::initializer_list容器。

完整代码

#include <array>
#include <iostream>
#include <random>
#include <range/v3/all.hpp>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <vector>

#include "fmt/format.h"
#include "fmt/ranges.h"

template <typename T>
decltype(auto) deref(T&& t) {
    return std::forward<T>(t).get();
}

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 : public ranges::iterator_t<Range> {
    using base = ranges::iterator_t<Range>;
    using value_type =
        std::remove_cvref_t<decltype(deref(*(std::declval<Range>().begin())))>;
    using difference_type = ranges::range_difference_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 main() {
    std::vector v{1, 2, 3, 4, 5, 6};

    // auto list =           {std::ref(v), std::ref(v)};  // 无法工作
    auto list = std::array{std::ref(v), std::ref(v)};  // 无法工作
    // auto list = std::vector{std::ref(v), std::ref(v)};  // 可以正常工作

    for (const auto& arr : list | ranges::views::deref) {
        fmt::print("{}\n", arr);
    }

    return 0;
}

处理std::array时的错误信息

<source>: In instantiation of 'struct deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator':
<source>:120:49:   required from here
<source>:66:29: error: base type 'ranges::iterator_t<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >' {aka 'std::reference_wrapper<std::vector<int> >*'} fails to be a struct or class type
   66 | struct deref_view< Range >::iterator: public ranges::iterator_t< Range > {
      |                             ^~~~~~~~
<source>: In function 'int main()':
<source>:120:49: error: no match for 'operator!=' (operand types are 'deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator' and 'deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator')
  120 |     for(const auto& arr : list | ranges::views::deref) {
      |                                                 ^~~~~
<source>: In instantiation of 'decltype(auto) deref_view<Range>::iterator::operator*() const [with Range = ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> >]':
<source>:120:49:   required from here
<source>:83:53: error: invalid 'static_cast' from type 'const deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator' to type 'deref_view<ranges::ref_view<std::array<std::reference_wrapper<std::vector<int> >, 2> > >::iterator::base' {aka 'std::reference_wrapper<std::vector<int> >*'}
   83 |    decltype(auto) operator*() const { return deref(*static_cast< base >(*this)); }
      |                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~

提问核心

我在ranges处理array和vector的机制上遗漏了哪些细节?(该问题在std::ranges中同样存在)


解答

错误根源:迭代器类型的本质差异

报错的直接原因是:

  • std::vector的迭代器是类模板实例(比如std::vector<T>::iterator),属于用户定义类型,可以被继承;
  • 而std::array的迭代器在多数编译器实现中是原生指针(因为std::array是固定大小的连续内存,编译器会做优化),指针是C++内置类型,不能作为基类被继承,这就是代码中struct iterator : public ranges::iterator_t<Range>报错的核心原因。

两种解决方案

方案1:用组合替代继承实现迭代器

放弃继承基础迭代器的方式,改为在自定义迭代器中持有基础迭代器的实例(组合模式),这样无论是类类型迭代器还是原生指针都能兼容。

修改后的iterator结构体代码:

template <ranges::range Range>
struct deref_view<Range>::iterator {
    using base = ranges::iterator_t<Range>;
    using value_type = std::remove_cvref_t<decltype(deref(*std::declval<base>()))>;
    using difference_type = ranges::range_difference_t<Range>;
    using pointer = std::add_pointer_t<value_type>;
    using reference = decltype(deref(*std::declval<base>()));
    using iterator_category = typename std::iterator_traits<base>::iterator_category;

    iterator() = default;
    explicit iterator(base it) : m_it(std::move(it)) {}

    // 后置++
    iterator operator++(int) {
        auto temp = *this;
        ++m_it;
        return temp;
    }

    // 前置++
    iterator& operator++() {
        ++m_it;
        return *this;
    }

    // 相等比较
    bool operator==(const iterator& other) const {
        return m_it == other.m_it;
    }

    // 解引用
    decltype(auto) operator*() const {
        return deref(*m_it);
    }

    // 根据需要实现其他迭代器操作:--, +=, -=, []等(取决于基础迭代器的类别)

private:
    base m_it;
};

同时需要修改deref_view的begin和end方法,显式构造自定义迭代器:

iterator begin() { return iterator(ranges::begin(m_base)); }
iterator end() { return iterator(ranges::end(m_base)); }

方案2:复用ranges::views::transform简化实现

实际上,deref视图的本质就是对范围中的每个元素应用deref函数,完全可以直接复用ranges-v3提供的transform视图,无需自己从零实现视图和迭代器,避免重复造轮子的错误。

简化后的代码:

#include <array>
#include <iostream>
#include <range/v3/all.hpp>
#include <vector>

#include "fmt/format.h"
#include "fmt/ranges.h"

template <typename T>
decltype(auto) deref(T&& t) {
    return std::forward<T>(t).get();
}

struct deref_fn {
    template <typename Rng>
    auto operator()(Rng&& rng) const {
        return ranges::views::all(std::forward<Rng>(rng)) | ranges::views::transform(deref);
    }

    template <typename Rng>
    friend auto operator|(Rng&& rng, deref_fn const&) {
        return ranges::views::all(std::forward<Rng>(rng)) | ranges::views::transform(deref);
    }
};

namespace ranges::views {
constexpr deref_fn deref{};
}

int main() {
    std::vector v{1, 2, 3, 4, 5, 6};

    auto list = std::array{std::ref(v), std::ref(v)};
    // auto list = {std::ref(v), std::ref(v)}; // 现在也能正常工作
    // auto list = std::vector{std::ref(v), std::ref(v)};

    for (const auto& arr : list | ranges::views::deref) {
        fmt::print("{}\n", arr);
    }

    return 0;
}

总结

  • 核心遗漏点:不同容器的迭代器类型可能存在本质差异,std::vector迭代器是类类型,而std::array迭代器可能是原生指针,原生指针无法被继承;
  • 自定义视图时,优先选择组合模式封装基础迭代器,或者直接复用现有标准视图(如transform),避免因迭代器类型差异导致的兼容性问题。

内容的提问来源于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.11 04:44:54