2013-01-24 42 views
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{} 
}; 

所以我遞歸訪問節點,直到我在葉到達。上面的問題是我必須爲每個葉子的operater()添加一個存根。你可以看到我有五個這樣的存根,但在實踐中有更多。你能提出一種模板化這個存根的方法嗎?

+0

如果你的葉類包含了一些類型定義或爲static你可以使用SFINAE C常量值 –

回答

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 
    { 
     ... 
    } 
}; 
+0

感謝您的回覆Andy,但我正在使用C++ 98作爲標記在我的問題中。 std :: enable_if帶有C++ 11,如果我沒有弄錯 – Baz

+0

@Baz:是提升一個選項? 'std :: enable_if'實際上是受'boost :: enable_if'啓發的, –

+0

是的,boost是一個選項,謝謝! – Baz

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