Шаблон для обхода узла дерева с использованием boost :: Вариант

Вот мой дизайн для обхода дерева узлов:

struct Leaf1{};
struct Leaf2{};
struct Leaf3{};
struct Leaf4{};
struct Leaf5{};

typedef boost::variant< Leaf4, Leaf5 > Node3;
typedef boost::variant< Leaf2, Leaf3, Node3> Node2;
typedef boost::variant< Node2, Leaf1 > Node1;

class NodeVisitor: public boost::static_visitor<void>
{
public:
template<class Node>
void operator()(const Node& e) const
{
boost::apply_visitor( *this, e );
}

void operator()(const Leaf1& e) const{}
void operator()(const Leaf2& e) const{}
void operator()(const Leaf3& e) const{}
void operator()(const Leaf4& e) const{}
void operator()(const Leaf5& e) const{}
};

Поэтому я рекурсивно посещаю узлы, пока не достигну листа. Проблема выше заключается в том, что я должен добавить заглушку для оператора () для каждого листа. Вы можете видеть, что у меня есть пять таких заглушек выше, но на практике их гораздо больше. Можете ли вы предложить способ шаблонирования этой заглушки?

3

Решение

РЕШЕНИЕ 1: Техника на основе SFINAE

Это решение основано на том факте, что невозможность замены параметров шаблона во время создания шаблона не приводит к ошибке компиляции (Ошибка замены не является ошибкой): вместо этого этот шаблон просто игнорируется для разрешения перегрузки. Таким образом, с помощью некоторого трюка вы можете выбрать, какие перегрузки определенного шаблона функции должны быть видны в зависимости от аргументов шаблона, предоставляемых во время создания экземпляра.

При использовании этого метода важно убедиться, что различающие условия, которые определяют видимость каждой перегрузки, взаимоисключающий, или двусмысленность может возникнуть.

Для начала вам нужно определить метафункцию черты, которая поможет вам определить, является ли определенный класс листом:

// Primary template
template<typename T> struct is_leaf<T> { static const bool value = false; };

// Specializations...
template<> struct is_leaf<Leaf1> { static const bool value = true; };
template<> struct is_leaf<Leaf2> { static const bool value = true; };
...

Затем вы можете использовать std::enable_if (или же boost::enable_if если вы работаете с C ++ 98), выберите, какую перегрузку оператора вызова следует сделать видимой:

class NodeVisitor: public boost::static_visitor<void>
{
public:

// Based on the fact that boost::variant<> defines a type list called
// "types", but any other way of detecting whether we are dealing with
// a variant is OK
template<typename Node>
typename std::enable_if<
!is_same<typename Node::types, void>::value
>::type
operator()(const Node& e) const
{
boost::apply_visitor( *this, e );
}

// Based on the fact that leaf classes define a static constant value
// called "isLeaf", but any other way of detecting whether we are dealing
// with a leaf is OK
template<typename Leaf>
typename std::enable_if<is_leaf<Leaf>::value>::type
operator()(const Leaf& e) const
{
...
}
};

РЕШЕНИЕ 2: техника на основе перегрузки

Если вы работаете на C ++ 98 и не хотите использовать boost::enable_if в качестве замены std::enable_ifальтернативный подход заключается в использовании разрешения перегрузки и неиспользуемого аргумента для различения двух перегрузок вспомогательной функции. Прежде всего, вы определяете два фиктивных класса:

struct true_type { };
struct false_type { };

Затем вы создаете свой is_leaf<> снова метафункция, должным образом специализируя ее для листовых классов:

// Primary template
template<typename T> struct is_leaf<T> { typedef false_type type; };

// Specializations...
template<> struct is_leaf<Leaf1> { typedef true_type type; };
template<> struct is_leaf<Leaf2> { typedef true_type type; };
...

Наконец, вы создаете экземпляр одного из этих фиктивных типов, чтобы выбрать правильную перегрузку вспомогательной функции process():

class NodeVisitor: public boost::static_visitor<void>
{
public:

template<typename T>
void operator()(const T& e) const
{
typedef typename is_leaf<T>::type helper;
process(e, helper());
}

template<typename Node>
void process(const Node& e, false_type) const
{
boost::apply_visitor(*this, e);
}

template<typename Leaf>
void process(const Leaf& e, true_type) const
{
...
}
};
3

Другие решения

Используйте дополнительный уровень косвенности для LeafN класс, что-то вроде:

template <typename LeafType>
struct LeafHolder
{
// has real instance of leaf..
};

Затем переопределите тип вашего варианта

typedef boost::variant< LeafHolder<Leaf4>, LeafHolder<Leaf5> > Node3;
typedef boost::variant< LeafHolder<Leaf2>, LeafHolder<Leaf3>, Node3> Node2;
typedef boost::variant< Node2, LeafHolder<Leaf1 > Node1;

Теперь ваш посетитель может стать:

class NodeVisitor: public boost::static_visitor<void>
{
public:
template<class Node>
void operator()(const Node& e) const
{
boost::apply_visitor( *this, e );
}

// single function to handle all leaves...
template <typename LeafType>
void operator()(const LeafHolder<LeafType>& e) const{}
};
2