chitay-knigi.com » Разная литература » C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 68 69 70 71 72 73 74 75 76 ... 121
Перейти на страницу:
meme>. Затем мы использовали конструкцию istream_iterator<pair<string, meme>>{cin}, чтобы получить такие элементы из стандартного потока ввода и передать их в ассоциативный массив с помощью inserter(m, end(m)).

При десериализации элементов типа meme из потока мы разрешили использовать пробелы в названиях и описаниях. Это легко реализовать, однако они не длиннее одной строки, поскольку мы поместили данные поля в кавычки. Взгляните на пример формата строк: "Name with spaces" "Description with spaces" 123.

При работе со строками, заключенными в кавычки как на входе, так и на выходе, поможет std::quoted. Если у нас есть строка s, то при выводе ее с помощью cout << quoted(s) она будет заключена в кавычки. В случае десериализации строки из потока, например используя cin >> quoted(s), мы считаем следующий символ кавычек, заполним строку символами, стоящими после него, и будем делать это до тех пор, пока не увидим следующий символ кавычек, независимо от количества встреченных пробелов.

Последний необычный момент заключается в том, как мы передали функцию max_func в наш вызов алгоритма accumulate:

auto max_func ([](size_t old_max, const auto &b) {

  return max(old_max, b.first.length());

});

size_t width {accumulate(begin(m), end(m), 0u, max_func)};

Похоже, что функция max_func принимает аргумент типа size_t и еще один аргумент с автоматическим типом, который оказывается элементом типа pair, взятым из ассоциативного массива. На первый взгляд все выглядит очень странно, поскольку большинство бинарных функций сжатия принимают аргументы идентичных типов, а затем объединяют их с помощью некой операции, как, например, это делает std::plus. В нашем случае все выглядит по-другому, поскольку мы не объединяем сами пары. Мы только получаем длину каждой строки, представляющей собой ключ, для каждой пары, отбрасываем остальное, а затем сжимаем полученные значения типа size_t с помощью функции max.

В вызове accumulate первый вызов функции max_func получает значение 0u, изначально предоставленное нами в качестве левого аргумента, и ссылку на первый элемент типа pair с правой стороны. Это дает возвращаемое значение max(0u, string_length), которое станет левым аргументом для следующего вызова, где очередная пара будет представлять собой правый параметр, и т.д.

Выводим любые данные на экран с помощью итераторов std::ostream

 Очень легко вывести что-то на экран с помощью потоков вывода, поскольку в STL есть много полезных перегруженных версий оператора << для большинства простых типов. Таким образом, структуры данных, содержащие элементы подобных типов, можно легко вывести на экран, задействовав класс std::ostream_iterator, что мы довольно часто делали.

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

Как это делается

В этом примере мы поработаем с итератором std::ostream_iterator, объединив его с новым пользовательским классом, и взглянем на его возможности неявного преобразования, что может помочь при выводе данных на экран.

1. Сначала указываем, какие заголовочные файлы включить, а затем объявляем об использовании пространства имен std:

#include <iostream>

#include <vector>

#include <iterator>

#include <unordered_map>

#include <algorithm>

using namespace std;

2. Реализуем функцию преобразования, которая соотносит числа и строки. Она будет возвращать строку "one" для значения 1, "two" для значения 2 и т.д.:

string word_num(int i) {

3. Мы заполним ассоциативный массив, основанный на хешах, этими парами, чтобы получить к ним доступ позже:

  unordered_map<int, string> m {

    {1, "one"}, {2, "two"}, {3, "three"},

    {4, "four"}, {5, "five"}, //...

  };

4. Теперь можно передать в функцию find ассоциативного массива, основанного на хеше, аргумент i и вернуть то значение, которое она найдет. Если функция ничего не найдет — например, для заданного числа нет перевода, — то вернем строку "unknown":

  const auto match (m.find(i));

  if (match == end(m)) { return "unknown"; }

  return match->second;

};

5. Мы будем работать также со структурой bork. Она содержит только одно целое число и неявно создается на основе целого числа. Кроме того, она имеет функцию print, которая принимает ссылку на поток вывода и выводит строку "bork" столько раз, сколько указано в целочисленной переменной borks:

struct bork {

  int borks;

  bork(int i) : borks{i} {}

  void print(ostream& os) const {

    fill_n(ostream_iterator<string>{os, " "},

           borks, "bork!"s);

  }

};

6. Для использования функции bork::print перегрузим оператор << для объектов потока, чтобы они автоматически вызывали функцию bork::print, когда объекты типа bork попадают в поток вывода:

ostream& operator<<(ostream &os, const bork &b) {

  b.print(os);

  return os;

}

7. Теперь наконец можем начать реализовывать саму функцию main. Изначально просто создаем вектор, содержащий некоторые значения:

int main()

{

  const vector<int> v {1, 2, 3, 4, 5};

8. Для объектов типа ostream_iterator нужен параметр шаблона, который указывает, переменные какого типа они могут выводить. Если мы напишем ostream_iterator<T>, то в дальнейшем для вывода данных на экран будет применяться конструкция ostream& operator(ostream&, const T&). Именно это свойство мы и реализовали до типа bork. На сей раз просто выводим целые числа, поэтому специализация выглядит как ostream_iterator<int>. Для вывода информации на экран мы будем использовать поток cout, так что предоставим его в качестве параметра конструктора. Пройдем по вектору в цикле и присвоим каждый элемент i разыменованному итератору вывода. Именно так потоковые итераторы используются в алгоритмах STL.

  ostream_iterator<int> oit {cout};

  for (int i : v) { *oit = i; }

  cout << 'n';

9. Полученный от итератора результат нам подходит, но он выводит числа без каких-либо разделителей. Если мы хотим добавить пробелы-разделители между всеми выведенными элементами, то можем предоставить собственную строку с пробелами в качестве второго параметра конструктора итератора выводного потока. Данное действие позволит вывести строку "1, 2, 3, 4, 5, " вместо строки "12345". К сожалению, мы не

1 ... 68 69 70 71 72 73 74 75 76 ... 121
Перейти на страницу:

Комментарии
Минимальная длина комментария - 25 символов.
Комментариев еще нет. Будьте первым.