Делегаты на C++ | страница 10
>#define C_STATIC_DELEGATE_VOID COMBINE(CStaticDelegateVoid, SUFFIX)
>#define C_METHOD_DELEGATE_VOID COMBINE(CMethodDelegateVoid, SUFFIX)
>…
>template‹class TRet TEMPLATE_PARAMS›
>class C_STATIC_DELEGATE_VOID: public I_DELEGATE‹TRet TEMPLATE_ARGS› {
> …
> virtual DelegateRetVal‹TRet›::Type Invoke(PARAMS) {
> m_pFunc(ARGS);
> return 0;
> }
> …
>};
>template‹class TObj, class TRet TEMPLATE_PARAMS›
>class C_METHOD_DELEGATE_VOID: public I_DELEGATE‹TRet TEMPLATE_ARGS› {
> …
> virtual DelegateRetVal‹TRet›::Type Invoke(PARAMS) {
> (m_pObj-›*m_pMethod)(ARGS);
> return 0;}
> …
>};
ПРИМЕЧАНИЕ В этом месте может возникнуть соблазн избежать дублирования кода, породив класс CStaticDelegateVoidX от CStaticDelegateX и CMethodDelegateVoidX от CMethodDelegateX соответственно. К сожалению, это не будет работать. Хотя мы и переопределяем виртуальный метод Invoke в производных классах, теоретическая возможность обратиться к Invoke базовых классов сохраняется. Поэтому компилятор честно попытается сгенерировать их реализацию. А это в случае TRet=void в очередной раз приведёт к ошибке, которую мы пытаемся обойти. Поэтому дублирование кода в данном случае неизбежно.
Осталось сделать последний шаг - перегрузить функцию NewDelegate ещё двумя реализациями:
>template‹class TRet TEMPLATE_PARAMS›
>I_DELEGATE‹TRet TEMPLATE_ARGS›* NewDelegate(TRet (*pFunc)(PARAMS)) {
> return new C_STATIC_DELEGATE‹TRet TEMPLATE_ARGS›(pFunc);
>}
>template‹class TRet TEMPLATE_PARAMS›
>I_DELEGATE‹TRet TEMPLATE_ARGS›* NewDelegate(void (*pFunc)(PARAMS)) {
> return new C_STATIC_DELEGATE_VOID‹void TEMPLATE_ARGS›(pFunc);
>}
> // Аналогично для CMethodDelegate*
В этом месте нас поджидает ещё один сюрприз. В большинстве случаев этот код будет работать, как по маслу. Но при задании TRet=void возникнет неоднозначность при обращении к функции NewDelegate. Правила разрешения перегрузки шаблонов функций описаны в разделе 14.5.5.2 Стандарта языка C++. В соответствии с этими правилами вторая версия NewDelegate не считается более специализированной, чем первая, так как для вызова обоих вариантов функции не требуется неявных преобразований типа.
Чтобы разрешить эту неоднозначность, придётся ввести дополнительный параметр функции NewDelegate, по которому и будет выбираться нужная версия функции:
>// Параметр этого типа будет индикатором
>template‹int use›
>class UseVoid {};
>…
>template‹class TRet TEMPLATE_PARAMS›
>I_DELEGATE‹TRet TEMPLATE_ARGS›* NewDelegate(TRet (*pFunc)(PARAMS), UseVoid‹0›) {