Как да изведа по време на компилация корена на дърво на наследяване, общо за два типа, ако такова съществува?

Имам проблем, при който трябва да открия общия предшественик на два типа (с един или нула базови класове), ако съществува. Възможно ли е да се изгради черта на типа, за да се реши този проблем? В код:

template<typename T1, typename T2>
  struct closest_common_ancestor
{
  typedef XXX type;  // what goes here?
};

Предвид следните видове:

struct root {};
struct child1 : root {};
struct child2 : root {};
struct child3 : child2 {};
struct unrelated {};

closest_common_ancestor ще доведе до следните типове:

closest_common_ancestor<root, child1>::type == root
closest_common_ancestor<child1, child2>::type == root
closest_common_ancestor<child3, child1>::type == root
closest_common_ancestor<child3, child2>::type == child2
closest_common_ancestor<unrelated, child1>::type == error

Вярвам, че мога да разреша този проблем, ако мога да проверя дали даден тип има нула или един базов клас и ако е така, името на този тип. Възможно ли е това?


person Jared Hoberock    schedule 03.11.2011    source източник
comment
Не можете да инспектирате базови класове. Не, освен ако ръчно не добавите мета информация към всеки от вашите класове.   -  person K-ballo    schedule 03.11.2011
comment
Обърнете внимание, че за директна връзка това вече е възможно (is_base_of може да се приложи по отношение на основни C++03 блокове)   -  person Matthieu M.    schedule 03.11.2011
comment
Ако има 2 корена root1 и root2 и child1, child2 ги наследяват и двата (struct child1 : root1, root2 {};), за closest_common_ancestor ще бъде двусмислено какво да върне.   -  person kennytm    schedule 04.11.2011


Отговори (1)


Както K-ballo спомена, за съжаление е невъзможно да се получи списъкът с бази, които класът има (много лошо...).

Ако ръчно анотирате вашите класове (да речем, дефинирайте прост std::tuple<> изброяващ базите), тогава можете да използвате тази информация. По-просто, разбира се, би било да се използва черта:

template <typename> struct list_bases { typedef std::tuple<> type; };

След това можете да специализирате тази черта за вашите типове:

template <> struct list_bases<child1> { typedef std::tuple<root> type; };

И започвайки оттам, можете да започнете експеримент с намирането на предшественик... но това може да не е незабавно. Освен подробностите за изпълнението (получаване на базите рекурсивно, прилагане на избора на „разстояние“), очаквам проблем със „странни“ случаи.

Изборът на разстояние може в обичайната (линейна) йерархия на наследяване да бъде разрешен чрез използване на комбинация от is_base_of и is_same, но вземете предвид следната йерархия:

struct root1 {}; struct root2 {};

struct child1: root1 {}; struct child2: root2 {};

struct child12: root1, child2 {}; struct child21: root2, child1 {};

Сега child12 и child21 имат два общи предшественика: root1 и root2... кой е най-близкият?

Те са еквивалентни. Помислете, че добавям:

struct root3 {}; struct child31: root3, child1 {};

Тогава root1 е общ предшественик на child12, child21 и child31.

Ако обаче заобиколя дефиницията на closest_common_ancestor и произволно дефинирах, че closest_common_ancesotr<child12, child21> е root2, тогава не можах да намеря никакъв общ прародител с child31.

Ето защо моето предложение е да се изброят всички най-близки предшественици и да се използва tuple за прилагане на множествени операции.

person Matthieu M.    schedule 03.11.2011
comment
Благодаря. За моето приложение бих разрешил двусмислието, което споменавате, като настоявам, че всички разглеждани типове имат само нула или един основен тип. - person Jared Hoberock; 04.11.2011