Размер шрифта
-
+

Обратные вызовы в C++ - стр. 17

Другое дело, когда лямбда-выражение осуществляет захват переменных, в этом случае мы получаем мощный и гибкий инструмент управления контекстом. Однако использование таких выражений в качестве аргумента вызывает определенные сложности. Связано это с тем, что тип лямбда-выражения является анонимным. Как следствие, имя типа нам неизвестно, и мы не можем просто объявить переменную нужного типа и присвоить ей лямбда-выражение, как это происходит, например, с указателями или классами. Решается указанная проблема с помощью шаблонов, что будет рассмотрено позже в соответствующих главах. Забегая вперед, отметим, что для хранения лямбда-выражений можно объявлять шаблон с параметром – типом лямбда-выражения (п. 4.4.2) либо использовать специальные классы библиотеки STL (п. 4.6.1).

2.5.3. Исполнитель

Исполнитель реализовывается в виде лямбда-выражения, а передача его как аргумента инициатору зависит от способа реализации последнего. Если исполнитель реализован в виде шаблона класса (п. 4.4.2), лямбда-выражение должно присваиваться в конструкторе класса. В случае использования классов STL (п. 4.5.1) лямбда-выражение передается подобно любому другому аргументу. Подробно эти вопросы рассматриваются в разделе 4, посвященном использованию шаблонов.

2.5.4. Синхронный вызов

Инициатор для синхронного вызова с лямбда-выражением реализуется в виде шаблонной функции, параметром шаблона выступает тип аргумента. Подробно этот вопрос рассмотрен в п. 4.2.1.

2.5.5. Преимущества и недостатки

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


Табл. 6. Преимущества и недостатки обратных вызовов с помощью лямбда-выражения


Гибкое управление контекстом. Возможность захвата переменных предоставляет простые и удобные средства изменения контекста. Изменяя состав захваченных переменных, мы легко можем добавлять значения, необходимые для контекста, при этом нет необходимости изменять код инициатора. Захватив указатель this, мы получаем доступ к содержимому класса, т. е. фактически лямбда-выражение превращается в «метод внутри метода» (см. пример в Листинг 22). Элегантно, не правда ли?

Требует использования шаблонов. Использование шаблонов накладывает архитектурные ограничения на реализацию программных модулей. Это связанно с тем, что шаблоны не предполагают присутствие предварительно откомпилированного кода. Подробнее об этом мы будем говорить в соответствующей главе (4.7), посвященной ограничениям при использовании шаблонов.

Листинг 22. Лямбда-выражение с захватом указателя this

>class EventCounter

>{

>public:

>  void AddEvent(unsigned int event)

>  {

>    callCounter_++;

>    lastEvent_ = event;

>  }

>private:

>  unsigned int callCounter_ = 0;

>  int lastEvent_ = 0;

>};


>class Executor

>{

>public:

>  Executor(EventCounter* counter): counter_(counter)

>  {

>    auto lambda = [this](int eventID)

>    {

>      //It will be called by initiator

>      counter_->AddEvent(eventID);

>      processEvent(eventID);

>    };

>    //Setup lambda in initiator

>  }

>private:

>  EventCounter* counter_;

>  void processEvent(int eventID) {/*Do something*/}

>};

2.6. Итоги

В C++ обратные вызовы могут быть реализованы с помощью следующих конструкций:

• указатель на функцию;

• указатель на статический метод класса;

• указатель на метод-член класса;

Страница 17