Вариации на тему STL. Адаптер обобщенного указателя на функцию-член класса - страница 2

Шрифт
Интервал

стр.

>template

>struct mem_fun_t {

> explicit mem_fun_t(R (T::*pm_)()): pm(pm>_) {}

> R operator()(T *p) const {return ((p->*pm)());}

>private:

> R (T::*pm)();

>};


Все кажется идеальным для работы с указателями, но ведь обобщенный указатель – это не указатель, он не знает, что такое operator->*! Нужно явно узнать, на какой объект он ссылается и потом уже выполнять операцию ->*

>template

>struct gen_mem_fun_t {

> explicit gen_mem_fun_t(R (T::*pm_)()): pm(pm_) {}

> template 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

>gen_mem_fun_t gen_mem_fun(R (T::*pm)()) {

> return gen_mem_fun_t(pm);

>}

Проблемы с разными компиляторами

Специализация шаблонных функций – членов шаблонного класса

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

ПРИМЕЧАНИЕ К таким относятся, например, gcc-2.95 и gcc-2.96

Попробуем обойтись без них. Специализация в той или иной форме нам в любом случае понадобится, так что воспользуемся тем, что есть – частичной специализацией классов. Введем вспомогательный класс и специализируем его для особого случая обычных указателей.

>template

>struct gen_mem_fun_operator {

> R operator()(TT p, R (T::*pm)()) {return (p.operator->()->*pm)();}

>};


>template

>struct gen_mem_fun_operator {

> R operator()(T* p, R (T::*pm)()) {return (p->*pm)();}

>};


Тогда наш gen_mem_fun_t запишется так:

>tem>plate

>struct gen_mem_fun_t {

> explicit gen_mem_fun_t(R (T::*pm_)()): pm(pm_) {}

> template R operator()(TT p) {return gen_mem_fun_operator()(p, pm);}

>private:

> R (T::*pm)();

>};

Проблема “return void”

Посмотрим внимательнее на реализацию функции operator() в нашем адаптере. Что будет, если мы захотим в качестве типа возвращаемого значения функции использовать void? Наша функция запишется так: void operator() {return void;}. С точки зрения стандарта все хорошо, но все в нашем мире определяется стандартом: есть компиляторы, которые не воспринимают такую конструкцию как допустимую.

ПРИМЕЧАНИЕ Таков, к примеру, Microsoft Visual C++ 6.0/7.0

К счастью, на помощь нам опять приходит частичная специализация:

>template

>struct gen_mem_fun_operator {

> void operator()(TT p, void (T::*pm)()) {(p.operator->()->*pm)();}

>};


>template

>struct gen_mem_fun_operator {

> void operator()(T* p, void (T::*pm)()) {(p->*pm)();}

>};

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

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

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

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

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

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

>template

>struct gen_mem_fun_traits {

> template

> struct signature {

>  typedef gen_mem_fun_base_t base;


стр.

Похожие книги