C++17 STL Стандартная библиотека шаблонов — страница 10 из 119

 и конечный итераторы одного диапазона параметров. Однако в качестве третьего параметра мы всякий раз отправляем один параметр из пакета. В конечном счете функция складывает все результаты и возвращает их вызывающей стороне.

Ее можно использовать следующим образом:


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 предоставляет стеки, автоматически увеличивающиеся векторы, ассоциативные массивы и т.д. Таким образом, можно сконцентрироваться на нашем приложении и не изобретать велосипед. В целом каждому программисту С++ не повредит знакомство со всеми контейнера