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

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

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

> };

>};


>template<> struct gen_mem_fun_traits {

> template struct signature {

>  typedef void_gen_mem_fun_base_t base;

> };

>};


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

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

>template

>struct gen_mem_fun_base_t {

>protected:

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

>public:

> template R operator()(TT p) {return (p.operator->()->*pm)();}

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

>private:

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

>};


>template

>struct void_gen_mem_fun_base_t {

>protected:

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

>public:

> template void operator()(TT p) {(p.operator->()->*pm)();}

> template<> void operator()(T* p) {(p->*pm)();}

>private:

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

>};

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

>template

>struct gen_mem_fun_t: gen_mem_fun_traits::template signature::base {

> typedef gen_mem_fun_traits::template signature::base base_;

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

>};


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

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

>template

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

> return gen_mem_fun_t(pm);

>}

Заключение

Надеюсь, читатель понял, что создание адаптера как такового не было основной целью этой статьи, тем более что гораздо более общий вариант такого адаптера под названием bind находится в библиотеке boost. Основная задача, которая стояла передо мной, была такова: дать читателю некоторые навыки и умения, позволяющие не пасовать перед необходимостью внести какие-либо дополнения или изменения в STL, а также познакомить с некоторыми приемами, специфичными для C++ и полезными при необходимости работать с компиляторами, не вполне поддерживающими стандарты.


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Геймдизайн. Рецепты успеха лучших компьютерных игр от Super Mario и Doom до Assassin’s Creed и дальше

Что такое ГЕЙМДИЗАЙН? Это не код, графика или звук. Это не создание персонажей или раскрашивание игрового поля. Геймдизайн – это симулятор мечты, набор правил, благодаря которым игра оживает. Как создать игру, которую полюбят, от которой не смогут оторваться? Знаменитый геймдизайнер Тайнан Сильвестр на примере кейсов из самых популярных игр рассказывает как объединить эмоции и впечатления, игровую механику и мотивацию игроков. Познакомитесь с принципами дизайна, которыми пользуются ведущие студии мира! Создайте игровую механику, вызывающую эмоции и обеспечивающую разнообразие.


Обработка событий в С++

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


MFC и OpenGL

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


Симуляция частичной специализации

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


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.