и конечный итераторы одного диапазона параметров. Однако в качестве третьего параметра мы всякий раз отправляем один параметр из пакета. В конечном счете функция складывает все результаты и возвращает их вызывающей стороне.Ее можно использовать следующим образом:
std::vector v {1, 2, 3, 4, 5};
matches(v, 2, 5); // возвращает 2
matches(v, 100, 200); // возвращает 0
matches("abcdefg", 'x', 'y', 'z'); // возвращает 0
matches("abcdefg", 'a', 'd', 'f'); // возвращает 3
Как видите, вспомогательная функция
matches
довольно гибкая — ее можно вызвать для векторов или даже строк. Она также будет работать для списка инициализаторов, контейнеров std::list
, std::array
, std::set
и прочих!
Проверка успешности вставки нескольких элементов в множество
Напишем вспомогательную функцию, которая добавляет произвольное количество параметров в контейнер
std::set
и возвращает булево значение, показывающее, успешно ли прошла операция:
template
bool insert_all(T &set, Ts ... ts)
{
return (set.insert(ts).second && ...);
}
Как же это работает? Функция
insert
контейнера std::set
имеет следующую сигнатуру:
std::pair insert(const value_type& value);
Документация гласит, что при попытке вставить элемент функция
insert
вернет пару из iterator
и переменной bool
. Если вставка пройдет успешно, значение переменной будет равно true
. Итератор же в этом случае укажет на новый элемент множества, а в противном случае — на существующий элемент, который помешал вставке.Наша вспомогательная функция после вставки обращается к полю
.second
. Оно содержит переменную bool
, которая показывает, была ли вставка успешной. Если все полученные пары имеют значение true
, то все вставки прошли успешно. Свертка объединяет все результаты вставки с помощью оператора &&
и возвращает результат.Контейнер можно использовать следующим образом:
std::set my_set {1, 2, 3};
insert_all(my_set, 4, 5, 6); // Возвращает true
insert_all(my_set, 7, 8, 2); // Возвращает false, поскольку 2 уже присутствует
Обратите внимание: если мы попробуем вставить, например, три элемента, но в процессе окажется, что второй элемент вставить нельзя, свертка
&& ...
досрочно прекратит работать и оставшиеся элементы не будут добавлены:
std::set my_set {1, 2, 3};
insert_all(my_set, 4, 2, 5); // Возвращает false
// теперь множество содержит значения {1, 2, 3, 4}, без 5!
Проверка попадания всех параметров в заданный диапазон
Поскольку можно убедиться, что одна из переменных находится в конкретном диапазоне, можно сделать то же самое для нескольких переменных с помощью выражений свертки:
template
bool within(T min, T max, Ts ts)
{
return ((min <= ts && ts <= max) && ...);
}
Выражение
(min <= ts && ts <= max)
определяет, находится ли каждый элемент пакета параметров в диапазоне между min
и max
(включаяmin
и max
). Мы выбрали оператор &&
, чтобы свести все результаты булева типа к одному, который имеет значение true
только в том случае, если все отдельные результаты имеют такое же значение.Это работает следующим образом:
within( 10, 20, 1, 15, 30); // --> false
within( 10, 20, 11, 12, 13); // --> true
within(5.0, 5.5, 5.1, 5.2, 5.3) // --> true
Что интересно: эта функция очень гибкая, поскольку единственным требованием, которое она предъявляет к типам, служит возможность сравнения экземпляров с помощью оператора
<=
. Это требование выполняется, например, типом std::string
:
std::string aaa {"aaa"};
std::string bcd {"bcd"};
std::string def {"def"};
std::string zzz {"zzz"};
within(aaa, zzz, bcd, def); // --> true
within(aaa, def, bcd, zzz); // --> false
Отправка нескольких элементов в вектор
Кроме того, вы можете написать вспомогательную функцию, которая не обобщает никаких результатов, но обрабатывает несколько действий одного вида. Такими действиями могут быть вставки элементов в контейнер
std::vector
, поскольку они не возвращают никаких результатов (функция std::vector::insert()
сообщает об ошибке, генерируя исключения):
template
void insert_all(std::vector&vec, Ts ... ts)
{
(vec.push_back(ts), ...);
}
int main()
{
std::vector v {1, 2, 3};
insert_all(v, 4, 5, 6);
}
Обратите внимание: мы используем оператор «запятая» (
,
), чтобы распаковать пакет параметров в отдельные вызовы vec.push_back(...)
, не выполняя свертку для самого результата. Эта функция также хорошо работает в отношении пустого пакета параметров, поскольку оператор «запятая» имеет неявный нейтральный элемент, void()
, который означает «ничего не делать». Глава 2Контейнеры STL
В этой главе:
□ использование идиомы erase-remove для контейнера
std::vector
;□ удаление элементов из неотсортированного контейнера
std::vector
за время O(1);□ получение доступа к экземплярам класса
std::vector
быстрым или безопасным способом;□ поддержка экземпляров класса
std::vector
в отсортированном состоянии;□ вставка элементов в контейнер
std::map:
эффективно и в соответствии с условиями;□ исследование новой семантики подсказок для вставки элементов с помощью метода
std::map::insert
;□ эффективное изменение ключей элементов
std::map
;□ применение контейнера
std::unordered_map
для пользовательских типов;□ отбор повторно встречающихся слов из пользовательского ввода и вывод их на экран в алфавитном порядке с помощью контейнера
std::set
;□ реализация простого ОПН-калькулятора с использованием контейнера
std::stack
;□ подсчет частоты встречаемости слов с применением контейнера
std::map
;□ реализация вспомогательного инструмента для поиска очень длинных предложений в текстах с помощью
std::multimap
;□ реализация личного списка текущих дел с помощью
std::priority_queue
.Введение
В стандартной библиотеке С++ появилось большое количество стандартных контейнеров. Контейнер всегда содержит набор данных или объектов. Достоинство контейнеров в том, что их можно применять практически для всех объектов, поэтому нужно только выбрать правильные контейнеры для конкретного приложения. STL предоставляет стеки, автоматически увеличивающиеся векторы, ассоциативные массивы и т.д. Таким образом, можно сконцентрироваться на нашем приложении и не изобретать велосипед. В целом каждому программисту С++ не повредит знакомство со всеми контейнера