2013-05-07 93 views
3

我有這樣的(簡化的示例)的類:升壓multi_index容器與複合鍵,自定義比較器和局部索引搜索

class A { 
    public: 
    typedef boost::shared_ptr<A> Ptr; 
    const std::string& getID() const; 
    const std::string& getLabel() const; 
    bool getFlag() const; 
    float getValue() const; 
    private: 
    ... 
}; 

我需要通過的(id, label),並且也是一個獨特的組合索引的容器(label, flag, value)的獨特組合。我還需要按照標籤第一個索引對標籤進行排序,如果標誌爲真,則標誌後跟值減小,如果標誌爲假,則增加值。因此,在創建鍵提取後,我在做這樣的事情:

typedef boost::multi_index::composite_key< 
    Ptr, extractor_id, extractor_label 
> id_label_key; 
typedef boost::multi_index::composite_key< 
    Ptr, extractor_label, extractor_flag, extractor_value 
> label_flag_value_key; 
... 
typedef boost::multi_index_container<Ptr, 
    boost::multi_index::indexed_by< 
    boost::multi_index::ordered_unique< 
     boost::multi_index::tag<by_id_label 
     id_label_key 
    >, 
    boost::multi_index::ordered_unique< 
     boost::multi_index::tag<by_label_flag_value>, 
     label_flag_value_key, 
     Compare 
    >, 
    > 
> Items; 
typedef Items::index<by_label_flag_value>::type Items_by_label_flag_value; 

,其中比較被定義爲:現在

struct Compare { 
    bool operator() (const boost::multi_index::composite_key_result<label_flag_value_key>& k, const boost::tuple<float,bool>& q) const { 
    return compare(k.value->getLabel(), k.value->getFlag(), k.value->getValue(), 
     q.get<0>(), q.get<1>(), q.get<2>() 
    } 
    bool operator() (const boost::tuple<float,bool>& q, const boost::multi_index::composite_key_result<label_flag_value_key>& k) const { 
    return compare(q.get<0>(), q.get<1>(), q.get<2>(), 
     k.value->getLabel(), k.value->getFlag(), k.value->getValue(), 
    } 
    bool operator() (const boost::multi_index::composite_key_result<label_flag_value_key>& k1, const boost::multi_index::composite_key_result<label_flag_value_key>& k2) const { 
    return compare(k1.value->getLabel(), k1.value->getFlag(), k1.value->getValue(), 
     k2.value->getLabel(), k2.value->getFlag(), k2.value->getValue()) 
    } 
    bool compare(const std::string& l1, bool f1, float v1, const std::string& l2, bool f2, float v2) const { 
    if (l1 != l2) return l1 < l2; 
    if (f1 != f2) return f1; 
    return f1 ? (v1 > v2) : (v1 < v2); 
    } 
}; 

,我可以這樣執行查詢:

Items_by_label_flag_value::const_iterator it = items_by_label_flag_value.find(boost::make_tuple("A", true, 0.1)); 

然而,如果我嘗試執行部分查詢 - 比方說,檢索具有相同標籤的所有項目 - 我的代碼將無法編譯:

std::pair<Items_by_label_flag_value::const_iterator, Items_by_label_flag_value::const_iterator> range = items_by_label_flag_value.equal_range(boost::make_tuple("A")); 

我知道它爲什麼不編譯:在比較器我明確地使用.get<0>().get<1>().get<2>()但局部搜索元沒有<1><2>元素。我不知道的是如何創建正確的比較器。如果我試圖向它添加兩個更多的函數,它只接受一個元素的元組,那麼編譯器會在調用operator()時抱怨含糊不清。

我也明白,composite_key_result應該是一個不透明的對象,我不應該使用它的內部。

所以我的問題是如何創建所需的索引和正確的比較?

回答

4

由於對於您的原始解決方案,您不需要爲第二個索引使用組合鍵作爲您的Compare ex拖拉機基本上試圖取代由composite_key/composite_key_compare自動生成的機器。下面一直(輕度)測試工作:

struct LBFCompare 
{ 
    bool operator() (const A& x, const A& y) const { 
    return compare(
     x.getLabel(), x.getFlag(), x.getValue(), 
     y.getLabel(), y.getFlag(), y.getValue()); 
    } 
    bool operator() (const std::string& x, const A& y) const{ 
    return compare(x,y.getLabel()); 
    } 
    bool operator() (const A& x, const std::string& y) const{ 
    return compare(x.getLabel(),y); 
    } 
    template<typename T0> 
    bool operator() (const boost::tuple<T0>& x, const A& y) const{ 
    return compare(x.get<0>(),y.getLabel()); 
    } 
    template<typename T0> 
    bool operator() (const A& x, const boost::tuple<T0>& y) const{ 
    return compare(x.getLabel(),y.get<0>()); 
    } 
    template<typename T0,typename T1> 
    bool operator() (const boost::tuple<T0,T1>& x, const A& y) const{ 
    return compare(x.get<0>(),x.get<1>(),y.getLabel(),y.getFlag()); 
    } 
    template<typename T0,typename T1> 
    bool operator() (const A& x, const boost::tuple<T0,T1>& y) const{ 
    return compare(x.getLabel(),x.getFlag(),y.get<0>(),y.get<1>()); 
    } 
    template<typename T0,typename T1,typename T2> 
    bool operator() (const boost::tuple<T0,T1,T2>& x, const A& y) const{ 
    return compare(x.get<0>(),x.get<1>(),x.get<2>(),y.getLabel(),y.getFlag(),y.getValue()); 
    } 
    template<typename T0,typename T1,typename T2> 
    bool operator() (const A& x, const boost::tuple<T0,T1,T2>& y) const{ 
    return compare(x.getLabel(),x.getFlag(),x.getValue(),y.get<0>(),y.get<1>(),y.get<2>()); 
    } 
    bool compare(const std::string& l1, const std::string& l2) const { 
    return l1 < l2; 
    } 
    bool compare(const std::string& l1, bool f1, const std::string& l2, bool f2) const { 
    if (l1 != l2) return l1 < l2; 
    return f1 < f2; 
    } 
    bool compare(const std::string& l1, bool f1, float v1, const std::string& l2, bool f2, float v2) const { 
    if (l1 != l2) return l1 < l2; 
    if (f1 != f2) return f1; 
    return f1 ? (v1 > v2) : (v1 < v2); 
    } 
}; 

struct by_id_label{}; 
struct by_label_flag_value{}; 

typedef boost::multi_index_container< 
    Ptr, 
    boost::multi_index::indexed_by< 
    boost::multi_index::ordered_unique< 
     boost::multi_index::tag<by_id_label>, 
     id_label_key 
    >, 
    boost::multi_index::ordered_unique< 
     boost::multi_index::tag<by_label_flag_value>, 
     boost::multi_index::identity<A>, 
     LBFCompare 
    > 
    > 
> Items; 
typedef Items::index<by_label_flag_value>::type Items_by_label_flag_value; 

int main() 
{ 
    Items c; 
    c.insert(Ptr(new A("id","label",true,1.0))); 
    Items_by_label_flag_value& i=c.get<by_label_flag_value>(); 
    i.find("id"); 
    i.find(boost::make_tuple("id")); 
    i.find(boost::make_tuple("id",true)); 
    i.find(boost::make_tuple("id",true,1.0)); 
} 

你提到是由於這樣的事實,你可能通過傳遞元組const char*真是讓人不是完全形成std::string正在找了含糊不清的問題:在這種情況存在隱式轉換,看起來1,2和3大小的元組是同樣好的候選元素(對於元組恕我直言,這是一個實現問題)。解決方案是模擬那些帶有元組的元素。

2

非常有趣的問題!要解決它,我能想到的最簡單的方法是:將以下添加到您的類

class A { 
    ... 
    float getTaggedValue()const{return getFlag()?-getValue():getValue();} 
    ... 
}; 

,然後用常規的複合鍵裝備你的第二個指數(label,tag,tagged_value)當元組做搜索(l,t,v)你一定不要忘記有v否定如果標記爲真(多一點的努力,你可以有getTaggedValue返回一個特殊類型,說pair<bool, double>,讓你不小心直接一個未經檢查的浮動傳遞到元組。)

+0

謝謝!是的,我也是這樣做的(這也使得自定義比較器不必要),我認爲這是一個正確的解決方案。另一個類似的方法是有兩個變量來存儲值,一個用值填充,另一個用零填充,這取決於標誌;當值類型不是float時,這個方法會更好,但是對於複製而言相對昂貴的東西(例如某種Decimal),這樣我們總是可以在key提取器中返回const ref。然而,我仍然感興趣的是如何解決原始問題 - 或者爲什麼不能。 – Vladimir 2013-05-07 23:16:52