2011-08-24 23 views
3

我正在寫一個用於boost::unordered_map的散列函數,它將存儲boost::graph邊描述符。夠簡單。然而,無向和有向圖形邊緣必須以不同方式散列(至少在我的情況下,當圖形無向時,邊緣(u,v)(v,u)是等效的,因此map[(u,v)]map[(v,u)]必須指向相同的值)。我可以使用圖特徵類(boost::graph_traits<Graph>::directed_category)檢測定向性,但是如何使用模板定義不同的實現?按特性分類的功能

以下是我到目前爲止,但我不希望if子句。相反,我想要EdgeHash編譯operator()的不同版本,具體取決於directed_category的值。這怎麼能實現?

template <typename Graph> 
struct EdgeHash { 
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge; 
    std::size_t operator()(const Edge& e) const { 
     std::size_t hash = 0; 
     if(boost::is_same<boost::graph_traits<Graph>::directed_category, boost::directed_tag>::value) { 
      boost::hash_combine(hash, e.m_source); 
      boost::hash_combine(hash, e.m_target); 
     } else { 
      boost::hash_combine(hash, std::min(e.m_source, e.m_target)); 
      boost::hash_combine(hash, std::max(e.m_source, e.m_target)); 
     } 
     return hash; 
    } 
}; 

回答

1

將哈希置於以定向類別類型爲模板的單獨結構中。

template<typename Directed, typename Edge> 
struct Hasher { 
    static std::size_t edge_hash(const Edge& e) { 
     std::size_t hash = 0; 
     boost::hash_combine(hash, e.m_source); 
     boost::hash_combine(hash, e.m_target); 
     return hash; 
    } 
}; 

template<typename Edge> 
struct Hasher<boost::directed_tag, Edge> { 
    static std::size_t edge_hash(const Edge& e) { 
     std::size_t hash = 0; 
     boost::hash_combine(hash, std::min(e.m_source, e.m_target)); 
     boost::hash_combine(hash, std::max(e.m_source, e.m_target)); 
     return hash; 
    } 
}; 

template <typename Graph> 
struct EdgeHash { 
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge; 
    std::size_t operator()(const Edge& e) const { 
     return Hasher< 
      boost::graph_traits<Graph>::directed_category, 
      Edge>::edge_hash(e); 
    } 
}; 
+0

你的代碼有一些錯誤;在'Hasher'中一個被遺忘的'typename',不會在'operator()'中調用'edge_hash',並且反轉邏輯(這可能是我的錯誤......)。但修復它,它的工作。謝謝! – carlpett

+0

固定。另外,由於Hasher是一種實用類型,您可能想隱藏它,例如通過使它成爲EdgeHash的私有嵌套(成員)類。 – AndrzejJ

+0

太棒了!我已經把它放在一個匿名的命名空間中,所以這不是問題:) – carlpett

1

使用boost::enable_if,你可以做到這一點,但是,你必須專門化結構。例如(未經測試)

template <typename Graph, class Enable = void> 
struct EdgeHash { 
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge; 
    std::size_t operator()(const Edge& e) const { 
     std::size_t hash = 0; 
     boost::hash_combine(hash, std::min(e.m_source, e.m_target)); 
     boost::hash_combine(hash, std::max(e.m_source, e.m_target)); 
     return hash; 
    } 
}; 

template <typename Graph> 
struct EdgeHash<Graph, typename boost::enable_if<boost::is_same<boost::graph_traits<Graph>::directed_category, boost::directed_tag> >::type> 
{ 
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge; 
    std::size_t operator()(const Edge& e) const { 
     std::size_t hash = 0; 
     boost::hash_combine(hash, e.m_source); 
     boost::hash_combine(hash, e.m_target); 
     return hash; 
    } 
}; 
1

也許在實際的EdgeHash類中添加一個bool模板參數?例如:

template <typename Graph, bool is_directed> 
struct EdgeHashImpl { 
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge; 
    std::size_t operator()(const Edge& e) const { 
     std::size_t hash = 0; 
     boost::hash_combine(hash, e.m_source); 
     boost::hash_combine(hash, e.m_target); 
    return hash; 
    } 
}; 

template <typename Graph> 
struct EdgeHashImpl <Graph, false> 
{ 
    typedef typename boost::graph_traits<Graph>::edge_descriptor Edge; 
    std::size_t operator()(const Edge& e) const { 
     std::size_t hash = 0; 
     boost::hash_combine(hash, std::min(e.m_source, e.m_target)); 
     boost::hash_combine(hash, std::max(e.m_source, e.m_target)); 
     return hash; 
    } 
}; 


template <typename Graph> 
struct EdgeHash 
: public EdgeHashImpl 
    <Graph, boost::is_same<boost::graph_traits<Graph>::directed_category, boost::directed_tag>::value)> 
{};