📚 Hub Books: Онлайн-чтение книгРазная литератураC++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+
1 ... 82 83 84 85 86 87 88 89 90 ... 121
Перейти на страницу:
мы воспользуемся методом std::apply, чтобы распаковать все элементы из кортежа t. Если данный шаг выглядит слишком сложным, обратитесь к предыдущему примеру — он демонстрирует работу метода std::apply.

Функция zip для кортежей

Функция zip принимает два кортежа, но выглядит весьма сложной, несмотря на то что имеет очень четкую реализацию:

template <typename T1, typename T2>

auto zip(const T1 &a, const T2 &b)

{

  auto z ([](auto ...xs) {

    return [xs...](auto ...ys) {

      return tuple_cat(make_tuple(xs, ys) ...);

    };

  });

  return apply(apply(z, a), b);

}

Для лучшего понимания этого кода представьте, что кортеж a содержит значения 1, 2, 3, а кортеж b — значения 'a', 'b', 'c'.

В данном случае вызов apply(z, a) приведет к вызову z(1, 2, 3). Он вернет объект функции, который захватит значения 1, 2, 3 в набор параметров xs. В момент вызова с помощью apply(z(1,2,3),b) этот объект получит значения 'a', 'b', 'c', помещенные в набор параметров ys. По сути, действие аналогично прямому вызову z(1,2,3)('a', 'b', 'c').

О’кей, что произойдет теперь, когда у нас есть значения xs = (1, 2, 3) и ys = ('a', 'b', 'c')? Выражение tuple_cat(make_tuple(xs, ys) ...) сделает следующее (рис. 8.1).

Сначала элементы из наборов xs и ys будут сгруппированы попарно. Это «попарное чередование» выполняется в вызове make_tuple(xs,ys). Сначала мы получим список кортежей переменной длины по два элемента в каждом. Чтобы получить один большой кортеж, мы применяем вызов tuple_cat — в результате получаем большой сконкатенированный кортеж, содержащий все члены исходных кортежей, которые чередуются. 

Замена void* с использованием std::any для повышения безопасности типов

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

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

Еще одним дополнением к STL в C++17 является тип std::any. Он разработан для того, чтобы хранить переменные любого вида, и предоставляет средства, которые позволяют выполнить проверку, безопасную для типов, и получить доступ к данным.

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

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

В этом примере мы реализуем функцию, которая пробует вывести на экран какие-либо данные. В качестве ее типа аргумента служит тип std::any.

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

#include <iostream>

#include <iomanip>

#include <list>

#include <any>

#include <iterator>

using namespace std;

2. Чтобы сократить объем использования угловых скобок в следующей программе, определим псевдоним для типа list<int> и будем применять его впоследствии:

using int_list = list<int>;

3. Реализуем функцию, которая утверждает, что может вывести на экран любые данные. Обещание заключается вот в чем: она выводит любые данные, предоставленные как аргумент в виде переменной std::any:

void print_anything(const std::any &a)

{

4. Первое, что нужно сделать, — проверить, содержит ли аргумент какие-то данные, или же это пустой экземпляр типа any. Если он пуст, то нет смысла пытаться определить, как его выводить на экран.

  if (!a.has_value()) {

    cout << "Nothing.n";

5. Если он не пуст, то можно попробовать сравнивать его с разными типами до тех пор, пока не получим совпадение. Первым типом послужит тип string. Если это строка, то можно выполнить преобразование a к ссылке string с помощью std::any_cast и просто вывести его на экран. Из соображений эстетики мы поместим строку в кавычки:

  } else if (a.type() == typeid(string)) {

    cout << "It's a string: "

         << quoted(any_cast<const string&>(a)) << 'n';

6. Если это не string, то может быть int. При совпадении данного типа можно использовать преобразование any_cast<int>, чтобы получить реальное значение int:

  } else if (a.type() == typeid(int)) {

    cout << "It's an integer: "

         << any_cast<int>(a) << 'n';

7. std::any работает не только для простых типов наподобие string и int. В переменную any можно поместить и ассоциативный массив, список или экземпляр любого другого сложного типа данных. Посмотрим, являются ли входные данные списком целых чисел, и если да, то можем вывести его точно так же, как и любой другой список:

  } else if (a.type() == typeid(int_list)) {

    const auto &l (any_cast<const int_list&>(a));

    cout << "It's a list: ";

    copy(begin(l), end(l),

         ostream_iterator<int>{cout, ", "});

    cout << 'n';

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

  } else {

    cout << "Can't handle this item.n";

  }

}

9. В функции main можем вызвать эту функцию с произвольными типами, с пустой переменной типа any с помощью {} или передать ей строку "abc" или целое число. Поскольку экземпляр типа std::any может быть создан на основе этих типов неявно, не возникает задержек, связанных с синтаксисом. Мы даже можем создать целый список и передать его в эту функцию:

int main()

{

  print_anything({});

  print_anything("abc"s);

  print_anything(123);

  print_anything(int_list{1, 2, 3});

10. Если мы будем помещать объекты, копировать которые действительно дорого, в переменную типа any, то можем также выполнить конструкцию «на месте» (in-place). Попробуем сделать это для нашего списочного типа. Выражение in_place_type_t<int_list>{} представляет собой пустой объект, дающий

1 ... 82 83 84 85 86 87 88 89 90 ... 121
Перейти на страницу:

Комментарии

Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?