3

ノードツリーをトラバースするための私の設計は次のとおりです。

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{}
};

そのため、リーフに到達するまでノードに再帰的にアクセスします。上記の問題は、リーフごとにoperator()のスタブを追加する必要があることです。上記にそのようなスタブが5つあることがわかりますが、実際にはもっとたくさんあります。このスタブをテンプレート化する方法を提案できますか?

4

2 に答える 2

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_ifC ++ 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:過負荷ベースの手法

boost::enable_ifC ++ 98で作業していて、の代わりに使用したくない場合std::enable_if、代替アプローチは、オーバーロード解決と、ヘルパー関数の2つのオーバーロードを区別するための未使用の引数を利用することです。まず、2つのダミークラスを定義します。

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; };
...

最後に、これらのダミー型の1つのインスタンスを作成して、ヘルパー関数の適切なオーバーロードを選択します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
    {
        ...
    }
};
于 2013-01-24T14:38:20.157 に答える
2

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{}
};
于 2013-01-24T14:36:31.160 に答える