Небольшой вопрос по шаблонам
От: Аноним  
Дата: 24.08.09 14:09
Оценка:
Всем добрый день.
Есть следующая структура, самое важное что все наиболее вложенные структуры наследуют от одного типа, что позволяет написать следующий код:

struct BHeader
{
};

struct All
{
   struct Part1
   {
       struct Part1Sub1 : BHeader
       {
       };
       struct Part1Sub2 : BHeader
       {
       };
   };
   struct Part2
   {
       struct Part2Sub1 : BHeader
       {
       };
       struct Part2Sub2 : BHeader
       {
       };
   };
};

class Processor
{
public:
    /// Общий случай
    template <typename T>
    void operator()(T::BHeader*)
    {
    }

    /// Частные случаи
    void operator()(All::Part1::Part1Sub1::BHeader*)
    {
    }
......
};


Но иногда хочется получить еще и средний вариант частного случая, наподобие:


template <typename T>
void operator()(All::Part1::T::BHeader*)
{
}


Подскажите плиз как можно добавить такой функционал.
Re: Небольшой вопрос по шаблонам
От: Аноним  
Дата: 24.08.09 14:43
Оценка: +1
не понял вообще, о каком функционале и частных случаях идет речь, если везде один и тот же тип и перегружать такое нельзя.
Re: Небольшой вопрос по шаблонам
От: alzt  
Дата: 25.08.09 06:07
Оценка:
Здравствуйте, Аноним, Вы писали:

А зачем здесь вообще использовать шаблоны?
Re: Небольшой вопрос по шаблонам
От: Кодт Россия  
Дата: 25.08.09 09:38
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Всем добрый день.

А>Есть следующая структура, самое важное что все наиболее вложенные структуры наследуют от одного типа, что позволяет написать следующий код:

Во-первых. Ограничение вывода типов С++ — это невозможность по зависимому типу определить его происхождение.
Только явно указывать, но в операторной форме это невозможно
struct X {};

struct Foo { typedef X Y; };
struct Bar { typedef X Y; };

template<class T> void test(typename T::Y) {}

struct Test
{
    template<class T> void operator()(typename T::Y) {}
};

int main()
{
    Foo::Y y; // X y;
    
    test(y); // ошибка
    test<Foo>(y); // правильно
    test<Bar>(y); // опа! тоже прокатило!
    
    Test t;
    t(y); // ошибка
    t.operator()<Foo>(y); // правильно
}

Возможные возражения "уж в моём-то случае никаких неоднозначностей нет" с гневом отвергнем как неконструктивные. Компилятор в принципе не собирается решать уравнения типов.
Хотите сделать универсальный решатель в стиле Хиндли-Милнера — пользуйтесь ML/F#/Haskell.
Либо прописывайте решения уравнений в каждом частном случае.

Но, к счастью, здесь можно и без решения уравнений обойтись. Сделать всё на наследовании.
void operator() (BHeader* p)                         { common_case(p); }
void operator() (All::Part1::Part1Sub1* p)           { case_p1_s1(static_cast<BHeader*>(p)); }
void operator() (All::Part1::Part1SubBase* p) { case_p1_s(static_cast<BHeader*>(p)); }
// где все Part1::Part1SubN унаследованы от базы Part1SubBase - это такой маркер


Если тебе нужно всё-таки сохранить тип, а не зарезать его до BHeader / Part1Sub1 / Part1SubBase, то придётся прибегнуть к кое-каким трюкам. Например, SFINAE или вот такому (не знаю как называется)
template<class T> void operator() (T* p) { doit(p, (T*)0); }

template<class T> void doit(T* p, BHeader* _)                  { common_case(p); }
template<class T> void doit(T* p, All::Part1::Part1Sub1* _)    { case_p1_s1(p); }
template<class T> void doit(T* p, All::Part1::Part1SubBase* _) { case_p1_s(p); }


Наконец, если нужно выводить тип во время исполнения (т.е. когда на вход подаётся только BHeader) — для этого существует dynamic_cast.
Он будет работать, только если BHeader содержит виртуальные функции или виртуально унаследован.
void operator() (BHeader* p)
{
    // сперва идут самые конкретные версии
    if(All::Part1::Part1Sub1* q = dynamic_cast<All::Part1::Part1Sub1*>(p))
        case_p1_s1(q);
    else
    if(All::Part1::Part1SubBase* q = dynamic_cast<All::Part1::Part1SubBase*>(p))
        case_p1_s(q);
    else
        common_case(p);
}

Чтобы дважды не писать тип, можно сделать или макрос
#define TRY_DYNAMIC(t, q, p) t* q = dynamic_cast<t*>(p)

    .....
    if(TRY_DYNAMIC(All::Part1::Part1Sub1, q, p))
        case_p1_s1(q);
    else
    .....

или универсальный каст
template<class From> // указатель или ссылка
struct dyncast_t
{
    From from_;
    dyncast_t(From f) : from_(f) {}

    template<class To> operator To* () const { return dynamic_cast<To*>(from_); }
    template<class To> operator To& () const { return dynamic_cast<To&>(from_); }
};

template<class From> dyncast_t<From&> dyncast(From& from) { return dyncast_t<From&>(from); }
template<class From> dyncast_t<From*> dyncast(From* from) { return dyncast_t<From*>(from); }

    .....
    if(All::Part1::Part1Sub1* q = dyncast(p))
        case_p1_s1(q);
    else
    .....
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.