explicit mem_fun_t(R (T::*pm_)()): pm(pm
_)
{}
R operator()(T *p) const {return ((p->*pm)());}
private:
R (T::*pm)();
};
Все кажется идеальным для работы с указателями, но ведь обобщенный указатель – это не указатель, он не знает, что такое operator->*! Нужно явно узнать, на какой объект он ссылается и потом уже выполнять операцию ->*
template<class R, class T>
struct gen_mem_fun_t {
explicit gen_mem_fun_t(R (T::*pm_)()): pm(pm_) {}
template<class TT> R operator()(TT p) {return (p.operator->()->*pm)();}
private:
R (T::*pm)();
};
Правда, возникает другая одна проблема – если теперь мы захотим использовать наш адаптер с обычным указателем, то потерпим поражение: обычные указатели не понимают operator->(). Таким образом, нам необходимо специализировать нашу функцию operator() для работы с обычными указателями:
template<>
R operator()(T* p) {
return (p->*pm)();
}
Реализация gen_mem_fun
Теперь реализация gen_mem_fun становится тривиальной:
template<class R, class T>
gen_mem_fun_t<R, T> gen_mem_fun(R (T::*pm)()) {
return gen_mem_fun_t<R, T>(pm);
}
Проблемы с разными компиляторами
Специализация шаблонных функций – членов шаблонного класса
К сожалению, вышеприведенный код не будет компилироваться на компиляторах, не поддерживающих специализацию шаблонов-функций – членов шаблонов классов.
ПРИМЕЧАНИЕ К таким относятся, например, gcc-2.95 и gcc-2.96
Попробуем обойтись без них. Специализация в той или иной форме нам в любом случае понадобится, так что воспользуемся тем, что есть – частичной специализацией классов. Введем вспомогательный класс и специализируем его для особого случая обычных указателей.
template<class R, class T, class TT>
struct gen_mem_fun_operator {
R operator()(TT p, R (T::*pm)()) {return (p.operator->()->*pm)();}
};
template<class R, class T>
struct gen_mem_fun_operator<R, T, T*> {
R operator()(T* p, R (T::*pm)()) {return (p->*pm)();}
};
Тогда наш gen_mem_fun_t запишется так:
tem
pl
ate<class R, class T>
struct gen_mem_fun_t {
explicit gen_mem_fun_t(R (T::*pm_)()): pm(pm_) {}
template<class TT> R operator()(TT p) {return gen_mem_fun_operator<R, T, TT>()(p, pm);}
private:
R (T::*pm)();
};
Проблема “return void”
Посмотрим внимательнее на реализацию функции operator() в нашем адаптере. Что будет, если мы захотим в качестве типа возвращаемого значения функции использовать void? Наша функция запишется так: void operator() {return void;}. С точки зрения стандарта все хорошо, но все в нашем мире определяется стандартом: есть компиляторы, которые не воспринимают такую конструкцию как допустимую.
ПРИМЕЧАНИЕ Таков, к примеру, Microsoft Visual C++ 6.0/7.0
К счастью, на помощь нам опять приходит частичная специализация:
template<class T, class TT>
struct gen_mem_fun_operator<void, T, TT> {
void operator()(TT p, void (T::*pm)()) {(p.operator->()->*pm)();}
};
template<class T>
struct gen_mem_fun_operator<void, T, T*> {
void operator()(T* p, void (T::*pm)()) {(p->*pm)();}
};
Частичная специализация
К сожалению, не все компиляторы поддерживают частичную специализацию шаблонных классов.
ПРИМЕЧАНИЕ К таким относится и Microsoft Visual C++ 6.0/7.0
Для решения этой проблемы можно использовать паттерн «traits», специфичный для C++. К