Почему доступ по диапазону для std::pair был удалён в C++11?
Я только что обнаружил, что в одном из черновиков C++11 были перегрузки stdbegin
и stdend
для std::pair
, которые позволяли рассматривать пару итераторов как диапазон, подходящий для использования в цикле на основе диапазонов (N3126, раздел 20.3.5.5), но это было удалено.
Кто-нибудь знает, почему это было убрано?
Я считаю это удаление очень неудачным, потому что, похоже, нет другого способа рассматривать пару итераторов как диапазон. Действительно:
- Правила поиска begin/end в цикле на основе диапазонов говорят о том, что begin/end ищутся в 1) как методы объекта диапазона 2) как свободные функции в "ассоциированных пространствах имен"
std::pair
не имеет методов begin/end- Единственное ассоциированное пространство имен для
std::pair<T, U>
в общем случае - это пространство имен std - Нам нельзя перегружать
std::begin
/std::end
дляstd::pair
самостоятельно - Мы не можем специализированно определить
std::begin
/std::end
дляstd::pair
(поскольку специализация должна быть частичной, а это не допускается для функций)
Есть ли какой-то другой способ, о котором я не знаю?
2 ответ(ов)
Вы можете использовать boost::make_iterator_range
. Этот метод создает iterator_range
, который имеет методы begin()
и end()
. boost::make_iterator_range
может принимать std::pair
итераторов в качестве аргумента.
В вашем коде реализована обертка над парой итераторов с использованием C11, что упрощает работу с ними и позволяет использовать более удобный интерфейс. Давайте подробнее рассмотрим, как именно вы можете использовать оптимизации C11 и в чем преимущества такого подхода.
Объяснение кода
Структура range_t: Это структура, наследующая от
std::pair
. Она содержит два итератора и предоставляет методыbegin()
иend()
, что позволяет использовать экземплярыrange_t
в диапазонных циклах (range-based for loops).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; } };
Здесь используется
std::forward
для идеальной передачи аргументов и обеспечения эффективного перемещения.Функция range: Вы определили две функции
range
, которые упрощают создание объектовrange_t
. Первая функция принимает пару итераторов, а вторая – два отдельных итератора. Обратите внимание на использованиеstd::make_pair
для создания пары.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))); }
Использование
std::move
здесь позволяет избежать лишнего копирования итераторов.Пример использования: В основной функции вы используете
std::multiset
, и благодаря вашей реализации можно элегантно выделить элементы, которые находятся в заданном диапазоне:multiset<int> mySet { 6,4,5,5,5,3,3,67,8,89,7,5,45,4,3 }; for (const auto&i : range(mySet.lower_bound(5), mySet.upper_bound(10))) { cout << i << ","; }
Этот код будет выводить все элементы, которые находятся между 5 и 10, включая 5.
Подсчет и сумма: Также вы можете подсчитать количество вхождений определенного элемента и их сумму:
for (const auto& i: range(mySet.equal_range(5))) { ++count; sum += i; }
Пример показывает, как легко вы можете использовать ваш класс
range_t
в сочетании с функциейequal_range
.
Заключение
Ваша реализация range_t
является отличным примером использования C++11 для создания более удобного и безопасного кода с использованием семантики перемещения и того, как можно работать с диапазонами. Такой подход повышает читаемость кода и упрощает его использование, особенно когда это касается работы с итераторами и контейнерами.
Оценивается ли условие в цикле `for` диапазона C++11 на каждой итерации?
Когда действительно стоит использовать noexcept?
Возможно ли вывести тип переменной в стандартном C++?
Почему `std::initializer_list` не поддерживает оператор подиндексации?
Стоит ли игнорировать предупреждение "-Wmissing-braces" от gcc/clang?