Подтвердить что ты не робот

Почему в std:: pair нет итераторов?

Почему std::pair не имеет итераторов?

std::pair должен предоставлять iterator и const_iterator, а также begin() и end() - только для своих двух членов.

Я думаю, что это было бы полезно, потому что тогда мы могли бы передать их в шаблонные функции, которые ожидают итерации, например vector или set.

Есть ли недостатки в этом?

4b9b3361

Ответ 1

Одна из причин заключается в том, что два элемента пары могут быть разных типов. Это не соответствует модели итератора.

То же самое касается кортежей, где, возможно, итераторы будут более привлекательными.

Если вам нужен недорогой однородный контейнер фиксированной длины, вы можете использовать std::array<T, n>.

Ответ 2

Цель std::pair не должна быть традиционным контейнером, а скорее служить кортежем, который позволяет рассматривать два потенциально гетерогенных объекта как один.

Кроме того, поскольку у вас есть прямой доступ к обеим частям пары, а также потому, что типы, парные, не могут быть одинаковыми, итератор не имеет смысла.

Ответ 3

Я не думаю, что есть какой-то другой недостаток, кроме того, что он работает только для pair<T,T>, а не pair<T,U>.

#include <utility>
#include <iterator>
#include <vector>
#include <iostream>

namespace itpair {
    template <typename T>
    struct pair_iterator : std::iterator<std::forward_iterator_tag, T> {
        std::pair<T,T> *container;
        int idx;
        pair_iterator(std::pair<T,T> *container, int idx) : container(container), idx(idx) {}
        T &operator*() const {
            return idx ? container->second : container->first;
        }
        T *operator->() const {
            return &*this;
        }
        friend pair_iterator &operator++(pair_iterator &self) {
            self.idx += 1;
            return self;
        }
        friend pair_iterator operator++(pair_iterator &self, int) {
            pair_iterator result = self;
            ++self;
            return result;
        }
        friend bool operator==(const pair_iterator &self, const pair_iterator &other) {
            return self.container == other.container && self.idx == other.idx;
        }
        friend bool operator!=(const pair_iterator &self, const pair_iterator &other) {
            return !(self == other);
        }
    };

    template <typename T>
    pair_iterator<T> begin(std::pair<T,T> &p) {
        return pair_iterator<T>(&p, 0);
    }
    template <typename T>
    pair_iterator<T> end(std::pair<T,T> &p) {
        return pair_iterator<T>(&p, 2);
    }
}

int main() {
    std::pair<int,int> p = std::make_pair(1, 2);
    using namespace itpair;
    std::vector<int> v(begin(p), end(p));
    std::cout << v[0] << " " << v[1] << "\n";
}

Конечно, вы тоже хотите const_iterator, а затем вам захочется получить произвольный доступ (что означает больше операторов).

Как все говорят, однако, это не то, для чего pair. Это просто не Контейнер.

Ответ 4

Я придумал это решение. Не очень "сексуальный", но он должен работать:

#include <type_traits>
#include <iterator>
#include <utility>

#include <boost/optional.hpp>

namespace pair_iterator {

template <class A, class B, class Pair>
class PairIterator {
public:
    using iterator_category = std::random_access_iterator_tag;
    using value_type = std::common_type_t<A, B>;
    using difference_type = std::ptrdiff_t;
    using pointer = std::add_pointer_t<value_type>;
    using reference = std::add_lvalue_reference_t<value_type>;
    using const_reference = std::add_lvalue_reference_t<const value_type>;
private:
    boost::optional<Pair &> pair = {};
    difference_type index = 2;
public:
    PairIterator(
        const boost::optional<Pair &> &pair = {},
        difference_type index = 2
    ) : pair(pair), index(index) {}

    // Iterator

    PairIterator(PairIterator&&) = default;
    PairIterator(const PairIterator&) = default;
    PairIterator &operator =(PairIterator&&) = default;
    PairIterator &operator =(const PairIterator&) = default;
    ~PairIterator() = default;

    void swap(PairIterator &other) {
        std::swap(pair, other.pair);
        std::swap(index, other.index);
    }

    reference operator *() {
        return index == 0 ? pair->first : pair->second;
    }

    const_reference operator *() const {
        return index == 0 ? pair->first : pair->second;
    }

    PairIterator &operator ++() {
        ++index;
        return *this;
    }

    // InputIterator

    bool operator ==(const PairIterator &other) const {
        return index == other.index;
    }

    bool operator !=(const PairIterator &other) const {
        return index != other.index;
    }

    PairIterator operator ++(int) const {
        return { pair, index+1 };
    }

    // ForwardIterator

    // BidirectionalIterator

    PairIterator &operator --() {
        --index;
        return *this;
    }

    PairIterator operator --(int) const {
        return { pair, index-1 };
    }

    // RandomAccessIterator

    PairIterator &operator +=(difference_type n) {
        index += n;
        return *this;
    }

    PairIterator operator +(difference_type n) const {
        return { pair, index+n };
    }

    PairIterator &operator -=(difference_type n) {
        index -= n;
        return *this;
    }

    PairIterator operator -(difference_type n) const {
        return { pair, index-n };
    }

    difference_type operator -(const PairIterator &other) const {
        return index - other.index;
    }

    reference operator [](difference_type n) {
        return (index+n) == 0 ? pair->first : pair->second;
    }

    const_reference operator [](difference_type n) const {
        return (index+n) == 0 ? pair->first : pair->second;
    }

    bool operator <(const PairIterator &other) const {
        return index < other.index;
    }

    bool operator >(const PairIterator &other) const {
        return index > other.index;
    }

    bool operator <=(const PairIterator &other) const {
        return index <= other.index;
    }

    bool operator >=(const PairIterator &other) const {
        return index >= other.index;
    }
};

template <class A, class B>
auto begin(std::pair<A, B> &pair) ->
PairIterator<A, B, std::pair<A, B>> {
    return { pair, 0 };
}

template <class A, class B>
auto end(std::pair<A, B> &pair) ->
PairIterator<A, B, std::pair<A, B>> {
    return { pair, 2 };
}

template <class A, class B>
auto begin(const std::pair<A, B> &pair) ->
PairIterator<const A, const B, const std::pair<A, B>> {
    return { pair, 0 };
}

template <class A, class B>
auto end(const std::pair<A, B> &pair) ->
PairIterator<const A, const B, const std::pair<A, B>> {
    return { pair, 2 };
}

} // namespace pair_iterator

namespace std {

using pair_iterator::begin;
using pair_iterator::end;

} // namespace std