"Вариации на тему STL. Адаптер обобщенного указателя на функцию-член класса" - читать интересную книгу автора (Гусаров Михаил)

Частичная специализация

К сожалению, не все компиляторы поддерживают частичную специализацию шаблонных классов.

ПРИМЕЧАНИЕ К таким относится и Microsoft Visual C++ 6.0/7.0

Для решения этой проблемы можно использовать паттерн «traits», специфичный для C++. К сожалению, он не сможет помочь в случае, когда один из параметров шаблона специализируется типом, зависящим от другого параметра шаблона, но в случае проблемы «return void» он помочь сможет.

ПРИМЕЧАНИЕ Вопрос, реально ли вообще симулировать частичную специализацию шаблонов, где специализируемый параметр шаблона зависит от неспециализируемого, на компиляторе, не поддерживающем частичную специализацию шаблонов и поддерживающем специализацию вообще только для глобальных классов и функций, остается открытым. Я такой возможности не вижу. Таким образом, создать без помощи препроцессора код нашего адаптера, компилирующийся и под gcc и под Visual C++, не представляется возможным.

Введем вспомогательный класс

templatelt;class Rgt;

struct gen_mem_fun_traits {

 templatelt;class Tgt;

 struct signature {

  typedef gen_mem_fun_base_tlt;R, Tgt; base;

 };

};


templatelt;gt; struct gen_mem_fun_traitslt;voidgt; {

 templatelt;class Tgt; struct signature {

  typedef void_gen_mem_fun_base_tlt;Tgt; base;

 };

};


Этот класс специализирован для специального случая функции, возвращающей void. Таким образом, хоть нам и придется ввести дополнительный класс для функций, возвращающих void, для клиента это будет выглядеть единообразно: gen_mem_fun_traitslt;rettypegt;::signaturelt;memberclassgt;::base.

Сами по себе ветви вычислений различных вариантов тривиальны:

templatelt;class R, class Tgt;

struct gen_mem_fun_base_t {

protected:

 gen_mem_fun_base_t(R (T::*pm_)()): pm(pm_) {}

public:

 templatelt;class TTgt; R operator()(TT p) {return (p.operator-gt;()-gt;*pm)();}

 templatelt;gt; R operator()(T* p) {return (p-gt;*pm)();}

private:

 R (T::*pm)();

};


templatelt;class Tgt;

struct void_gen_mem_fun_base_t {

protected:

 void_gen_mem_fun_base_t(void (T::*pm_)()): pm(pm_) {}

public:

 templatelt;class TTgt; void operator()(TT p) {(p.operator-gt;()-gt;*pm)();}

 templatelt;gt; void operator()(T* p) {(p-gt;*pm)();}

private:

 void (T::*pm)();

};

Теперь определим сам gen_mem_fun_t:

templatelt;class R, class Tgt;

struct gen_mem_fun_t: gen_mem_fun_traitslt;Rgt;::template signaturelt;Tgt;::base {

 typedef gen_mem_fun_traitslt;Rgt;::template signaturelt;Tgt;::base base_;

 explicit gen_mem_fun_t(R (T::*pm_)()): base_(pm_) {}

};


Один момент здесь требует пояснения: typedef используется для того, чтобы компилятор понял, какому предку нужно передать в конструктор наш указатель на функцию-член.

И, наконец, gen_mem_fun вообще остался без изменений:

templatelt;class R, class Tgt;

gen_mem_fun_tlt;R, Tgt; gen_mem_fun(R (T::*pm)()) {

 return gen_mem_fun_tlt;R, Tgt;(pm);

}