C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц
В нашем выражении свертки мы всегда передаем в функцию std::count начальный и конечный итераторы одного диапазона параметров. Однако в качестве третьего параметра мы всякий раз отправляем один параметр из пакета. В конечном счете функция складывает все результаты и возвращает их вызывающей стороне.
Ее можно использовать следующим образом:
std::vector<int> 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 <typename T, typename ... Ts>
bool insert_all(T &set, Ts ... ts)
{
return (set.insert(ts).second && ...);
}
Как же это работает? Функция insert контейнера std::set имеет следующую сигнатуру:
std::pair<iterator, bool> insert(const value_type& value);
Документация гласит, что при попытке вставить элемент функция insert вернет пару из iterator и переменной bool. Если вставка пройдет успешно, значение переменной будет равно true. Итератор же в этом случае укажет на новый элемент множества, а в противном случае — на существующий элемент, который помешал вставке.
Наша вспомогательная функция после вставки обращается к полю .second. Оно содержит переменную bool, которая показывает, была ли вставка успешной. Если все полученные пары имеют значение true, то все вставки прошли успешно. Свертка объединяет все результаты вставки с помощью оператора && и возвращает результат.
Контейнер можно использовать следующим образом:
std::set<int> my_set {1, 2, 3};
insert_all(my_set, 4, 5, 6); // Возвращает true
insert_all(my_set, 7, 8, 2); // Возвращает false, поскольку 2 уже присутствует
Обратите внимание: если мы попробуем вставить, например, три элемента, но в процессе окажется, что второй элемент вставить нельзя, свертка && ... досрочно прекратит работать и оставшиеся элементы не будут добавлены:
std::set<int> my_set {1, 2, 3};
insert_all(my_set, 4, 2, 5); // Возвращает false
// теперь множество содержит значения {1, 2, 3, 4}, без 5!
Проверка попадания всех параметров в заданный диапазон
Поскольку можно убедиться, что одна из переменных находится в конкретном диапазоне, можно сделать то же самое для нескольких переменных с помощью выражений свертки:
template <typename T, typename Ts>
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 <typename T, typename ... Ts>
void insert_all(std::vector<T> &vec, Ts ... ts)
{
(vec.push_back(ts), ...);
}
int main()
{
std::vector<int> 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 предоставляет стеки, автоматически увеличивающиеся векторы, ассоциативные массивы и т.д. Таким образом,