auto lambda = [capture](int eventID) {/*this is a body of lambda*/};
lambda(10); //lambda call
Однако указанный способ не будет работать, когда требуется сохранить лямбда-выражение в классе. Мы не можем объявить переменную – член класса с типом auto, потому что это означало бы объявление переменной заранее не определенного типа, что не допускается.
Организовать хранение лямбда-выражения внутри класса можно с помощью шаблона, в котором тип выражения будет параметризован. Однако при инстанциировании шаблона переменной, предназначенной для хранения, значение должно быть присвоено в конструкторе. Это нельзя сделать позже, потому что в объекте-замыкании, генерируемым компилятором, запрещен оператор присваивания. Это и понятно: поскольку тип каждого объявленного лямбда-выражения является уникальным, то мы не можем ему ничего присваивать, кроме самого себя.
Добавим в реализацию инициатора, описанного в Листинг 37 п. 4.4.1, два конструктора. Один конструктор будет с переменной – аргументом обратного вызова для инициализации члена класса. Другой конструктор будет без аргументов (конструктор по умолчанию), чтобы оставить возможность отложенной настройки (Листинг 41).
Листинг 41. Инициатор с дополнительными конструкторами
template
class Initiator
{
public:
Initiator() {}
Initiator(const CallbackArgument& argument) : callbackHandler(argument) {}
void setup(const CallbackArgument& argument)
{
callbackHandler = argument;
}
void run()
{
int eventID = 0;
//Some actions
callbackHandler(eventID);
}
private:
CallbackArgument callbackHandler;
};
Для любых типов аргументов обратного вызова, кроме лямбда-выражений, допускается использование обоих конструкторов. Для лямбда-выражений допускается использование только конструктора с аргументом, при попытке использования конструктора по умолчанию компилятор выдаст ошибку. Кроме того, в этом случае нельзя будет вызвать метод setup – также будет сгенерирована ошибка. Таким образом, использование инициатора с лямбда-выражением не предполагает динамической модификации: настройка происходит один раз в конструкторе при инстанциировании шаблона, и больше изменить ее нельзя 20 20 Указанная проблема решается при использовании универсального аргумента, о чем пойдет речь в следующей главе
.
А какой тип аргумента нам указывать при инстанциировании шаблона, ведь тип лямбда-выражения является анонимным? Для этой цели мы будем использовать ключевое слово decltype, которое возвращает тип объявленной переменной (см. Листинг 42).
Листинг 42.Инстанциирование шаблона асинхронного обратного вызова для лямбда-выражения
int capture = 10;
auto lambda = [capture](int eventID) {/*this is a body of lambda*/};
Initiator callbackLambda1 (lambda); // Ok, initialization in constructor
Initiator callbackLambda = lambda; // Ok, implicit constructor call
Initiator callbackLambda2; //Error: attempting to reference a deleted function
callbackLambda.setup(lambda); //Error: ‘operator’ = attempting to reference a deleted function
callbackLambda.run();
В Листинг 43 приведены примеры реализации исполнителя для различных типов аргументов. Объявления класса CallbackConverterпредставлены в Листинг 27 и Листинг 28 п. 4.2.2, инициатор используется из Листинг 41 п. 4.4.2.
Листинг 43. Исполнитель для шаблона-инициатора с различными типами аргумента
class Executor // (1)
{
public:
static void staticCallbackHandler(int eventID, Executor* executor) {}
void callbackHandler(int eventID) {}
void operator() (int eventID) {}
};
void ExternalHandler(int eventID, void* somePointer) {} // (2)
int main()
{
Executor executor; // (3)
int capturedValue = 0;
// (4) Pointer to the external function
using PtrExtFunc = void(*) (int, void*); // (5)
using CallbackExtFunction = CallbackConverter; // (6)
Initiator initExtFunction; // (7)
initExtFunction.setup(CallbackExtFunction(ExternalHandler, &executor)); // (8)
// (9) Pointer to the static method
using PtrStaticMethod = void(*) (int, Executor*); // (10)
using CallbacStaticMethod = CallbackConverter; // (11)
Initiator initStaticMethod; // (12)
initStaticMethod.setup(CallbacStaticMethod(Executor::staticCallbackHandler, &executor)); // (13)
// (14) Pointer to the class member method
using PtrMethod = void(Executor::*)(int); // (15)
using CallbackMemberMethod = CallbackConverter; // (16)
Initiator initMemberMethod; // (17)
initMemberMethod.setup(CallbackMemberMethod(&executor, &Executor::callbackHandler)); // (18)
// (19) Functional object
Initiator initFunctionObject; // (20)
initFunctionObject.setup(executor); // (21)
// (22) Lambda-expression
auto lambda = [capturedValue](int eventID) {/*Body of lambda*/}; // (23)
Читать дальше