我有一个这样的课程(简化示例):
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;
其中Compare定义为:
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>
和{{1元素。我不知道的是如何创建正确的比较器。如果我尝试再添加两个函数,只接受一个元素的元组,那么编译器会在<2>
调用中抱怨含糊不清。
我也明白operator()
应该是一个不透明的对象,我不应该使用它的内部。
所以我的问题是如何创建所需的索引和正确的比较器?
答案 0 :(得分:4)
对于原始解决方案,您不需要为第二个索引使用复合键,因为Compare
提取器基本上是在尝试替换由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*
s而不是完全形成std::string
s的元组来查找:在这种情况下有隐式转换和看似一,二,三大小的元组同样是好的候选者(元组恕我直言的实现问题。)解决方案是对那些带元组的LBFCompare::operator()
进行模板化。
答案 1 :(得分:2)
非常有趣的问题!我能想到的最简单的解决方法是:将以下内容添加到您的班级
class A {
...
float getTaggedValue()const{return getFlag()?-getValue():getValue();}
...
};
然后在(label,tag,tagged_value)
上使用常规复合键为您的第二个索引配备当使用元组(l,t,v)
进行搜索时,如果标记为true,则不要忘记v
否定(使用多一点努力,你可以让getTaggedValue
返回一个特殊的类型,比如pair<bool, double>
,这样你就不会无意中直接将未经检查的浮点数传递给元组。)