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

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

Шрифт:

-
+

Интервал:

-
+
1 ... 34 35 36 37 38 39 40 41 42 ... 121
Перейти на страницу:
0;

}

static bool ends_with_b (const std::string &s)

{

  return s.rfind("b") == s.length() - 1;

}

3. Теперь реализуем вспомогательную функцию и назовем ее combine. Она принимает бинарную функцию в качестве первого параметра — это может быть, например, логическое И либо логическое ИЛИ. Затем она принимает два других параметра, представляющих собой две функции-предиката, которые нужно объединить:

template <typename A, typename B, typename F>

auto combine(F binary_func, A a, B b)

{

4. Просто возвращаем лямбда-выражение, которое захватывает новую комбинацию предикатов. Оно направляет параметр обоим предикатам, а затем помещает результаты работы их обоих в бинарную функцию и возвращает ее результат:

  return [=](auto param) {

    return binary_func(a(param), b(param));

  };

}

5. Укажем, что будем использовать пространство имен std с целью сэкономить немного времени при написании функции main:

using namespace std;

6. Теперь объединим две функции-предиката в другую функцию-предикат, говорящую, начинается ли заданная строка с символа a и заканчивается ли символом b, как, например, строки "ab" или "axxxb". На роль бинарной функции выбираем std::logical_and. Это шаблонный класс, экземпляр которого нужно создавать, вследствие чего будем использовать его вместе с фигурными скобками. Обратите внимание: мы не предоставляем дополнительный параметр шаблона, поскольку для данного класса он по умолчанию будет равен void. Эта специализация класса самостоятельно выводит все типы параметров:

int main()

{

  auto a_xxx_b (combine(

    logical_and<>{},

    begins_with_a, ends_with_b));

7. Итерируем по стандартному потоку ввода и выводим на экран все слова, которые удовлетворяют условиям предиката:

  copy_if(istream_iterator<string>{cin}, {},

    ostream_iterator<string>{cout, ", "},

    a_xxx_b);

  cout << 'n';

}

8. Компиляция и запуск программы дадут следующий результат. Мы передаем программе четыре слова, но только два из них удовлетворяют условиям предиката:

$ echo "ac cb ab axxxb" | ./combine

ab, axxxb,

Дополнительная информация

Библиотека STL уже предоставляет несколько функциональных объектов наподобие std::logical_and, std::logical_or, а также множество других, поэтому не нужно реализовывать их в каждом проекте. Ознакомиться со справочным материалом по С++ и исследовать доступные варианты можно на http://en.cppreference.com/w/cpp/utility/functional.

Вызываем несколько функций с одинаковыми входными данными

 Существует множество задач, чьи решения требуют написания повторяющегося кода. Большую его часть можно легко заменить лямбда-выражениями, и очень просто создать вспомогательную функцию, которая будет оборачивать подобные повторяющиеся задачи.

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

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

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

1. Включим заголовочный файл, необходимый для вывода данных на экран:

#include <iostream>

2. Сначала реализуем функцию multicall, которая является основной для этого примера. Она принимает произвольное количество функций в качестве параметров и возвращает лямбда-выражение, принимающее один параметр. Она перенаправляет данный параметр всем функциям, предоставленным ранее. Таким образом, можно определить функцию auto call_all(multicall(f,g,h)), а затем вызов call_all(123) приведет к серии вызовов f(123); g(123); h(123);. Эта функция уже выглядит сложной, поскольку требуется распаковать набор параметров, в котором находятся функции, в набор вызовов с помощью конструктора std::initializer_list.

static auto multicall (auto ...functions)

{

  return [=](auto x) {

    (void)std::initializer_list<int>{

      ((void)functions(x), 0)...

    };

  };

}

3. Следующая вспомогательная функция принимает функцию f и набор параметров xs. После этого она вызывает функцию f для каждого из параметров. Таким образом, вызов for_each(f,1,2,3) приводит к серии вызовов: f(1); f(2); f(3);. Функция, по сути, использует такой же синтаксический прием для распаковки набора параметров xs в набор вызовов функций, как и функция, показанная ранее.

static auto for_each (auto f, auto ...xs) {

  (void)std::initializer_list<int>{

    ((void)f(xs), 0)...

  };

}

4. Функция brace_print принимает два символа и возвращает новый объект функции, принимающий один параметр x. Она выводит его на экран, окружив двумя символами, которые мы только что захватили.

static auto brace_print (char a, char b) {

  return [=] (auto x) {

    std::cout << a << x << b << ", ";

  };

}

5. Теперь наконец можно использовать все эти функции в функции main. Сначала определим функции f, g и h. Они представляют функции print, которые принимают значения и выводят их на экран, окружив разными скобками. Функция nl принимает любой параметр и просто выводит на экран символ переноса строки.

int main()

{

  auto f (brace_print('(', ')'));

  auto g (brace_print('[', ']'));

  auto h (brace_print('{', '}'));

  auto nl ([](auto) { std::cout << 'n'; });

6. Объединим все эти функции с помощью вспомогательной функции multicall:

  auto call_fgh (multicall(f, g, h, nl));

7. Мы хотим, чтобы каждое предоставленное нами число было выведено на экран трижды в разных скобках. Таким образом, нужно выполнить один вызов функции, который приведет к пяти вызовам нашей мультифункции, а та, в свою очередь, выполнит четыре вызова для функций f, g, h и nl:

  for_each(call_fgh, 1, 2, 3, 4, 5);

}

8. Перед компиляцией и запуском программы подумаем, какой результат должны получить:

$ ./multicaller

(1), [1], {1},

(2), [2], {2},

(3), [3], {3},

(4), [4], {4},

(5), [5], {5},

Как это работает 

Вспомогательные функции, которые мы только что реализовали, выглядят очень сложными. Так произошло из-за распаковки набора параметров с помощью std::initializer_list. Почему мы вообще использовали эту структуру данных? Еще раз взглянем на for_each:

auto for_each ([](auto f, auto

1 ... 34 35 36 37 38 39 40 41 42 ... 121
Перейти на страницу:

Комментарии

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

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