Быстрые делегаты 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() в этом случае. Аналогичная идея используется в облегченной проверке соответствия типов позже.