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

  • Проблема “return void”
  • Частичная специализация

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

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

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

    ПРИМЕЧАНИЕ К таким относятся, например, 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» он помочь сможет.

    ПРИМЕЧАНИЕ Вопрос, реально ли вообще симулировать частичную специализацию шаблонов, где специализируемый параметр шаблона зависит от неспециализируемого, на компиляторе, не поддерживающем частичную специализацию шаблонов и поддерживающем специализацию вообще только для глобальных классов и функций, остается открытым. Я такой возможности не вижу. Таким образом, создать без помощи препроцессора код нашего адаптера, компилирующийся и под 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 | Добавить материал | Нашёл ошибку | Наверх