Быстрые делегаты C++ - Связывание аргументов функции-члена
ОГЛАВЛЕНИЕ
Связывание аргументов функции-члена
CBase1 b1;
// создание копии
fd::delegate1 < void, int > dg1(&CBase1::foo, b1);
// сохраняем внутри клонированный связанный объект
fd::delegate1 < void, int > dg2(&CBase1::foo, &b1);
// сохраняем указатель на связанный объект
dg1(123); // (внутренняя копия b1).foo(123);
dg2(234); // (&b1)->foo(123);
// связывание члена
fd::delegate1 < void, int > dg3, dg4;
dg3.bind(&CBase1::bar, b1);
// сохраняем внутри клонированный связанный объект
dg4.bind(&CBase1::bar, &b1);
// сохраняем указатель на связанный объект
dg3(345); // (внутренняя копия b1).bar(345);
dg4(456); // (&b1)->bar(456);
// fd::bind() вспомогательная функция
fd::delegate1 < void, int > dg5 = fd::bind(&CBase1::foo, b1, _1);
// сохраняем внутри клонированный связанный объект
fd::delegate1 < void, int > dg6 = fd::bind(&CBase1::foo, &b1, _1);
// сохраняем указатель на связанный объект
dg5(567); // (внутренняя копия b1).foo(567);
dg6(678); // (&b1)->foo(678);
Указатель функции-члена должен вызываться для вызываемого объекта такого же типа. Вызываемый объект связан как ссылка (указатель), поэтому при вызове делегата он должен быть в достоверном состоянии. Вызывающий отвечает за то, чтобы сохранять целевой функтор незатронутым при вызове.
В новой версии связанный объект может быть клонирован внутри, или даже можно привязать интеллектуальный указатель, чтобы автоматически управлять памятью.
std::auto_ptr<CBase1> spb1(new CBase1);
fd::delegate1 < int, int > dg1;
dg1.bind(&CBase1::foo, spb1);
dg1(123);
// get_pointer(внутренняя копия spb1)->foo(123);
boost::shared_ptr<CBase1> spb2(new CBase1);
fd::delegate1 < int, int > dg2(&CBase1::foo, spb2);
dg2(234);
// get_pointer(внутренняя копия spb2)->foo(234);
Вспомогательная функция fd::bind() скопирована из идеи Джоди Хагинса для FastestDelegate Дона. Она позволяет легко переносить код с boost::function и boost::bind.
#include < boost/function.hpp >
#include < boost/bind.hpp >
using boost::function1;
using boost::bind;
CBase1 b1;
function1 < void, int > fn = bind( &CBase1::foo, &b1, _1 );
Вышеприведенный код можно легко преобразовать в:
#include "delegate.h"
using fd::delegate1;
using fd::bind;
CBase1 b1;
delegate1 < void, int > fn = bind( &CBase1::foo, &b1, _1 );
Но учтите, что метка-заполнитель _1 не работает здесь как boost::_1. Это всего лишь метка-заполнитель.
fd::make_delegate() вспомогательная функция
Она может быть полезна при передаче делегата как параметра функции.
typedef fd::delegate1 < void, int > MyDelegate1;
typedef fd::delegate2 < void, CDerived *, int > MyDelegate2;
void SomeFunction1(MyDelegate1 myDg) { }
void SomeFunction2(MyDelegate2 myDg) { }
CBase1 b1; CDerived1 d1("d1");
// версия свободной функции
SomeFunction1(fd::make_delegate(&::hello));
SomeFunction2(fd::make_delegate(&::hellohello);
// версия адаптера функции-члена
SomeFunction1(fd::make_delegate((CBase1 *)0, &CBase1::foobar));
SomeFunction2(fd::make_delegate((CDerived *)0, &CDerived1::foo));
// версия связывания аргументов функции-члена
SomeFunction1(fd::make_delegate(&CBase1::foo, &b1));
SomeFunction2(fd::make_delegate(&CDerived1::foofoobar, &d1);
SomeFunction1(fd::make_delegate(&CBase1::foo, b1));
SomeFunction2(fd::make_delegate(&CDerived1::foofoobar, d1);
Но версия адаптера функции-члена fd::make_delegate() должна трактоваться как отличная от другой версии fd::make_delegate(), и для этого есть причина.
Функция члена CBase1::virtual_not_overridden в этом примере – это публичная функция-член, и производный класс не заменяет ее. Так как это публичная функция-член, можно ссылаться на указатель функции-члена как на запись (обозначение) 'CDerived1::virtual_not_overridden'. Но когда эта запись указателя функции-члена передается к какой-либо автоматической шаблонной выводящей функции, такой как fd::make_delegate() в качестве аргумента, типом шаблона автоматически назначается 'CBase1::virtual_not_overridden', а не 'CDerived1::virtual_not_overridden'. Поэтому делегат, созданный из fd::make_delegate(), станет типом fd::delegate2 < void, CBase1 *, int >, в то время как нам нужен тип fd::delegate2 < void, CDerived1 *, int >. Вот почему нужно явно передавать типированный пустой указатель в качестве первого аргумента вспомогательной функции make_delegate() в этом случае. Аналогичная идея используется в облегченной проверке соответствия типов позже.