代码之家  ›  专栏  ›  技术社区  ›  Ivan Smirnov

具有复杂值类型的迭代器:与value\u类型和引用混淆

  •  4
  • Ivan Smirnov  · 技术社区  · 7 年前

    我想创建一个自定义迭代器包装器,例如, enumerate :给定类型上的一对迭代器 T ,它将返回一个iterable over类型 std::pair<const int, T&> ,其中该对的第一个元素将取值0、1、2,依此类推。

    我不知道应该是什么 value_type reference 我的迭代器。我想支持两种行为:

    首先,引用基础序列的值:

    for (auto& kv: enumerate(my_vec)) {
        kv.second = kv.first;
    }
    

    (有点 std::iota );

    第二,复制价值:

    std::vector<int> a{10, 20, 30};
    auto copy = *enumerate(a).begin();
    a[0] = 15;
    std::cout << copy.first << " " << copy.second; // 0 10
    

    我不知道应该返回什么类型的 Iterator::operator*() . 如果是的话 标准::成对(&L);const int,T&&燃气轮机; 那么在第二个示例中,将不会复制值。如果是的话 std::pair<const int, T> 那么在第一个示例中,不可能引用基础值。我应该做什么,应该做什么 value\u类型 , 参考 pointer 这样的迭代器的typedefs?

    下面是我实现它的尝试。它支持引用,但不支持复制。

    template<typename T>
    struct Iterator {
        using TT = typename std::iterator_traits<T>::value_type;
    
        using value_type = std::pair<const int, TT>;
        using reference = std::pair<const int&, typename std::iterator_traits<T>::reference>;
        using pointer = value_type*;
        using iterator_category = std::forward_iterator_tag;
        using difference_type = std::ptrdiff_t;
    
        std::pair<int, T> it;
        Iterator(T iterator) : it(0, iterator) {}
        bool operator==(const Iterator& other) const { return it.second == other.it.second; }
        bool operator!=(const Iterator& other) const { return it.second != other.it.second; }
        reference operator*() { return { it.first, *it.second }; }
        Iterator& operator++() { ++it.first; ++it.second; return *this; }
    };
    

    P、 我刚刚检查过,boost::Adapters::index也有同样的问题,并且没有复制值。

    1 回复  |  直到 7 年前
        1
  •  1
  •   Passer By    7 年前

    此问题类似于 std::vector<bool> ,您希望提供一个代理,该代理的行为类似于引用,但也支持值语义。

    但不同的是,涉及的类型没有限制,涉及到两个参考,各种各样的毛发都会弹出。以下是部分实现,它说明了您遇到的一些问题

    #include<iterator>
    #include<functional>
    
    template<typename F, typename S, bool defined = true>
    struct sfinae_difference_type {};
    
    template<typename F, typename S>
    struct sfinae_difference_type<F, S, 
            std::is_same_v<typename std::iterator_traits<F>::difference_type, 
                           typename std::iterator_traits<S>::difference_type>>
    {
        using difference_type = typename std::iterator_traits<F>::difference_type;
    };
    
    template<typename F, typename S>
    class pair_iterator : sfinae_difference_type<F, S>
    {
        using Fvalue_type = typename std::iterator_traits<F>::value_type;
        using Svalue_type = typename std::iterator_traits<S>::value_type;
        using Freference = typename std::iterator_traits<F>::reference;
        using Sreference = typename std::iterator_traits<S>::reference;
    
        F f;
        S s;
    
    public:
        using value_type = std::pair<Fvalue_type, Svalue_type>;
    
        struct reference
        {
            Freference first;
            Sreference second;
    
            reference() = delete;
            reference(const reference& other) : first{other.first}, second{other.second} {} 
            reference& operator=(const reference& rhs)
            {
                first = rhs.first;
                second = rhs.second;
                return *this;
            }
            operator value_type() { return {f, s}; }
    
        private:
            reference(Freference f, Sreference s) : first{f}, second{s} {}
            friend pair_iterator;
        };
    
        struct pointer
        {
            // similar to reference
        };
    
        pair_iterator() = default;
        pair_iterator(const pair_iterator&) = default;
        pair_iterator(F f, S s) : f{f}, s{s} {}
        pair_iterator& operator++() { ++f; ++s; return *this; }
        reference operator*() { return {*f, *s}; }
        pointer operator->() { return {f.operator->(), s.operator->()}; }
        bool operator==(const pair_iterator& other)
        {
            return f == other.f && s == other.s;
        }
    };
    

    然后将其用作

    #include<vector>
    #include<list>
    #include<iostream>
    
    int main()
    {
        std::vector v{1, 2, 3, 4, 5};
        std::list l{6, 7, 8, 9, 10};
        pair_iterator begin{v.begin(), l.begin()}, end{v.end(), l.end()};
        for(; begin != end; ++begin)
            std::cout << begin->first << ' ' << begin->second << '\n';
    }
    

    Live

    一些显而易见的问题:

    1. 实现是乏味的。拥有sfinae友好的类型别名和适当的代理需要大量的样板文件。
    2. 代理的语义可能令人困惑。复制/分配一个 reference 另一个意思?是什么 auto is_this_a_copy = *it 应该怎么做?
    3. 平等意味着什么?两个内部迭代器是否必须相等才能相等?这打破了与末端迭代器的比较。

    所有这些都必须经过努力才能奏效,而且没有一个简单的答案。