Почему не std::pair
есть итераторы?
std::pair
должен обеспечить iterator
а также const_iterator
так же как begin()
а также end()
— только для их двух членов.
Я думаю, что это было бы полезно, потому что тогда мы могли бы передать их в шаблонные функции, которые ожидают итераций, таких как vector
или же set
,
Есть ли минусы в этом?
Одна из причин заключается в том, что два элемента пары могут быть разных типов. Это не соответствует модели итератора.
То же самое касается кортежей, где наличие итераторов, возможно, будет еще более привлекательным.
Если вам нужен недорогой гомогенный контейнер фиксированной длины, вы можете использовать std::array<T, n>
,
Цель std::pair
не должен быть традиционным контейнером, а должен служить кортежем, который позволяет обрабатывать два потенциально гетерогенных объекта как один.
Кроме того, поскольку у вас есть прямой доступ к обеим частям пары, а типы пар не могут быть одинаковыми, итератор не имеет смысла.
Я не думаю, что есть какой-то конкретный недостаток, кроме того, что это работает только для 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
для. Это просто не контейнер.
Я придумал это решение. Не очень «сексуально», но должно работать
#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