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

Гусаров Михаил

Шрифт:

 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++. К сожалению, он не сможет помочь в случае, когда один из параметров шаблона специализируется типом, зависящим от другого параметра шаблона, но в случае проблемы «return void» он помочь сможет.

  • Читать дальше
  • 1
  • 2
  • 3

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: