Iterators with a complex value type: confusion with value_type and reference

I want to create a custom iterator shell, for example enumerate: given a pair of iterators by type T, it will return an iterable type std::pair<const int, T&>, where the first element of the pair takes values โ€‹โ€‹0, 1, 2, etc.

I had a problem to find out what should be value_typeand referencemy iterator. I want to support two behaviors:

First, referring to the values โ€‹โ€‹of the base sequence:

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

(view std::iota);

Secondly, by making a copy of the value:

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*(). std::pair<const int, T&>, . std::pair<const int, T>, . value_type, reference 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.S. , boost:: adapters:: index .

+4
1

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

:

  • . sfinae , .
  • . / reference ? auto is_this_a_copy = *it?
  • ? ? .

, , .

+1

Source: https://habr.com/ru/post/1694433/


All Articles