Всем добрый день.
Есть следующая структура, самое важное что все наиболее вложенные структуры наследуют от одного типа, что позволяет написать следующий код:
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*)
{
}
Подскажите плиз как можно добавить такой функционал.
не понял вообще, о каком функционале и частных случаях идет речь, если везде один и тот же тип и перегружать такое нельзя.
Здравствуйте, <Аноним>, Вы писали:
А>Всем добрый день.
А>Есть следующая структура, самое важное что все наиболее вложенные структуры наследуют от одного типа, что позволяет написать следующий код:
Во-первых. Ограничение вывода типов С++ — это невозможность по зависимому типу определить его происхождение.
Только явно указывать, но в операторной форме это невозможно
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>>