Почему был удален доступ к парам из С++ 11?
Я только что обнаружил, что в какой-то момент проект С++ 11 имел std::begin
/std::end
перегрузки для std::pair
, что позволяло обрабатывать пару итераторов как диапазон, подходящий для использования в цикле на основе диапазона (N3126, раздел 20.3.5.5), но с тех пор оно было удалено.
Кто-нибудь знает, почему он был удален?
Я считаю удаление очень неудачным, потому что, похоже, нет другого способа рассматривать пару итераторов как диапазон. Действительно:
- Правила поиска для начала/конца в цикле, основанной на диапазоне, говорят, что начало/конец рассматриваются в 1) как функции-члены объекта диапазона 2) как свободные функции в "связанных пространствах имен"
-
std::pair
не имеет функций начала/конца элемента
- Единственным связанным пространством имен для
std::pair<T, U>
в общем случае является пространство имен std
- Нам не разрешено перегружать
std::begin
/std::end
для std::pair
самих
- Мы не можем специализировать
std::begin
/std::end
для std::pair
(потому что специализация должна быть частичной и не допускаться для функций)
Есть ли другой способ, который мне не хватает?
Ответы
Ответ 1
Я думаю, что статья 2009 года "Пары не делают хороших диапазонов" Алисдейр Мередит является, по крайней мере, частью ответа. В принципе, многие алгоритмы возвращают пары итераторов, которые на самом деле не гарантируются как допустимые диапазоны. По этой причине, по этой причине они удалили поддержку pair<iterator,iterator>
из цикла for-range. Однако предлагаемое решение не было полностью принято.
Если вы точно знаете, что какая-то пара итераторов действительно представляет допустимый диапазон, вы можете привязать их к настраиваемому типу, который предлагает функции-члены begin()/end():
template<class Iter>
struct iter_pair_range : std::pair<Iter,Iter> {
iter_pair_range(std::pair<Iter,Iter> const& x)
: std::pair<Iter,Iter>(x)
{}
Iter begin() const {return this->first;}
Iter end() const {return this->second;}
};
template<class Iter>
inline iter_pair_range<Iter> as_range(std::pair<Iter,Iter> const& x)
{ return iter_pair_range<Iter>(x); }
int main() {
multimap<int,int> mm;
...
for (auto& p : as_range(mm.equal_range(42))) {
...
}
}
(непроверенные)
Я согласен, что это немного бородавка. Функции, возвращающие допустимые диапазоны (например, equal_range), должны сказать это, используя соответствующий тип возврата. Это немного смущает, что мы должны вручную подтвердить это с помощью чего-то вроде as_range
выше.
Ответ 2
Вы можете использовать boost::make_iterator_range
.
Он создает метод iterator_range с методами begin()
и end()
.
boost::make_iterator_range
может принимать std::pair
итераторов.
Ответ 3
расширяя указанный выше ответ, используя оптимизацию С++ 11:
#include <utility>
template<class Iter>
struct range_t : public std::pair<Iter, Iter> {
using pair_t = std::pair<Iter, Iter>;
range_t(pair_t&& src)
: std::pair<Iter, Iter>(std::forward<pair_t>(src))
{}
using std::pair<Iter, Iter>::first;
using std::pair<Iter, Iter>::second;
Iter begin() const { return first; }
Iter end() const { return second; }
};
template<class Iter>
range_t<Iter> range(std::pair<Iter, Iter> p) {
return range_t<Iter>(std::move(p));
}
template<class Iter>
range_t<Iter> range(Iter i1, Iter i2) {
return range_t<Iter>(std::make_pair(std::move(i1), std::move(i2)));
}
// TEST:
#include <iostream>
#include <set>
using namespace std;
int main() {
multiset<int> mySet { 6,4,5,5,5,3,3,67,8,89,7,5,45,4,3 };
cout << "similar elements: ";
for (const auto&i : range(mySet.lower_bound(5), mySet.upper_bound(10))) {
cout << i << ",";
}
cout << "\n";
int count = 0, sum = 0;
for (const auto& i: range(mySet.equal_range(5)))
{
++count;
sum += i;
}
cout << "5 appears " << count << " times\n"
<< "the sum is " << sum << "\n";
return 0;
}