#c #template-meta-programming
#c #шаблон-метапрограммирование
Вопрос:
У меня проблема, когда мне нужно обнаружить общего предка двух типов (с одним или нулевым базовым классом), если он существует. Возможно ли создать признак типа для решения этой проблемы? В коде:
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
Я полагаю, что смогу решить эту проблему, если смогу проверить, имеет ли тип нулевой или один базовый класс, и если да, то имя этого типа. Возможно ли это?
Комментарии:
1. Вы не можете проверять базовые классы. Нет, если вы вручную не добавите метаинформацию в каждый из своих классов.
2. Обратите внимание, что для прямой взаимосвязи это уже возможно (
is_base_of
может быть реализовано в терминах базовых блоков C 03)3. Если есть 2 корня
root1
иroot2
, иchild1
,child2
наследуют их оба (struct child1 : root1, root2 {};
), будет неоднозначно,closest_common_ancestor
что возвращать.
Ответ №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
для реализации операций set.
Комментарии:
1. Спасибо. Для моего приложения я бы разрешил двусмысленность, о которой вы упомянули, настаивая на том, что все рассматриваемые типы имеют только нулевой или один базовый тип.