|
||||
|
Специализация шаблонных функций – членов шаблонного класса Частичная специализация Проблемы с разными компиляторамиСпециализация шаблонных функций – членов шаблонного класса К сожалению, вышеприведенный код не будет компилироваться на компиляторах, не поддерживающих специализацию шаблонов-функций – членов шаблонов классов. ПРИМЕЧАНИЕ К таким относятся, например, 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 запишется так: 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 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++. К сожалению, он не сможет помочь в случае, когда один из параметров шаблона специализируется типом, зависящим от другого параметра шаблона, но в случае проблемы «return void» он помочь сможет. ПРИМЕЧАНИЕ Вопрос, реально ли вообще симулировать частичную специализацию шаблонов, где специализируемый параметр шаблона зависит от неспециализируемого, на компиляторе, не поддерживающем частичную специализацию шаблонов и поддерживающем специализацию вообще только для глобальных классов и функций, остается открытым. Я такой возможности не вижу. Таким образом, создать без помощи препроцессора код нашего адаптера, компилирующийся и под gcc и под Visual C++, не представляется возможным. Введем вспомогательный класс template<class R> struct gen_mem_fun_traits { template<class T> struct signature { typedef gen_mem_fun_base_t<R, T> base; }; }; template<> struct gen_mem_fun_traits<void> { template<class T> struct signature { typedef void_gen_mem_fun_base_t<T> base; }; }; Этот класс специализирован для специального случая функции, возвращающей void. Таким образом, хоть нам и придется ввести дополнительный класс для функций, возвращающих void, для клиента это будет выглядеть единообразно: gen_mem_fun_traits<rettype>::signature<memberclass>::base. Сами по себе ветви вычислений различных вариантов тривиальны: template<class R, class T> struct gen_mem_fun_base_t { protected: gen_mem_fun_base_t(R (T::*pm_)()): pm(pm_) {} public: template<class TT> R operator()(TT p) {return (p.operator->()->*pm)();} template<> R operator()(T* p) {return (p->*pm)();} private: R (T::*pm)(); }; template<class T> struct void_gen_mem_fun_base_t { protected: void_gen_mem_fun_base_t(void (T::*pm_)()): pm(pm_) {} public: template<class TT> void operator()(TT p) {(p.operator->()->*pm)();} template<> void operator()(T* p) {(p->*pm)();} private: void (T::*pm)(); }; Теперь определим сам gen_mem_fun_t: template<class R, class T> struct gen_mem_fun_t: gen_mem_fun_traits<R>::template signature<T>::base { typedef gen_mem_fun_traits<R>::template signature<T>::base base_; explicit gen_mem_fun_t(R (T::*pm_)()): base_(pm_) {} }; Один момент здесь требует пояснения: typedef используется для того, чтобы компилятор понял, какому предку нужно передать в конструктор наш указатель на функцию-член. И, наконец, 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); } |
|
||
Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх |
||||
|