【问题标题】:c++ set::find by object propertyc++ set::find by object property
【发布时间】:2013-02-28 20:47:50
【问题描述】:

我有一个object 和一个property,这是该对象的方法所必需的:

class Object {
   Property property;
}

然后我需要对这个对象进行分组。要求在该组中只有具有唯一属性的对象。在set 中分组这些对象是很自然的。我定义了一个operator< 或者如果它是unordered_set operator==hash()。没关系。然后另一个问题出现了。我需要使用property 在集合中找到这些对象。所以我需要创建一个Object 类型的对象,并将这个属性分配给它的字段。而这正是我不喜欢的地方。

另一种方法是创建map。并复制或创建指向对象属性的指针,然后将它们用作映射中的键。我认为这也是一个双重工作。

我想要set 之类的东西,但能够不使用整个对象进行搜索,而是使用对象的属性来查找它。例如,我定义如下:

class Object {
   Property property;
   bool operator<(Object obj) { return property < obj.property; }
   bool operator<(Property obj) { return property < obj.property; }
};
some::set<Object> objSet;

那么我可以做到以下几点:

Object obj;
objSet.insert(obj);
objSet.find(obj.property);

你能提供一些对我有帮助的容器实现吗? boost,qt 都可以接受。

【问题讨论】:

  • operator&lt; 是如何定义的?它是否使用property 作为主要标准?
  • operator&lt;, operator==, hash() 仅使用 property 字段。
  • 正是Boost.MultiIndex的用途。

标签: c++ qt boost map unordered-map


【解决方案1】:

Boost Multi-Index array 可能是您需要的。这个例子multiple sort on a single set 与您正在寻找的非常匹配。

typedef multi_index_container<
  Object,
  indexed_by<
    ordered_unique<member<Object,Property,&Object::property> >
  > 
> object_set;

object_set oset;
Property prop;
oset.find( prop );

【讨论】:

    【解决方案2】:

    您可以使用 std::equal_range 的 4 参数重载,提供一个比较器,将 PropertyObject 进行比较。

    struct Cmp
    {
        bool operator() ( const Object& o, const Property& p ) const
        {
            return o.property < p;
        }
        bool operator() ( const Property& p, const Object& o ) const
        {
            return p < o.property;
        }
    };
    
    std::set<Object> s = ....;
    Property property_val = ....;
    auto r = std::equal_range(s.begin(),s.end(), property_val, Cmp());
    

    另一种选择是将std::find_if 与合适的一元谓词一起使用。但是您不会从 std::setstd::equal_range 的对数查找中受益。

    【讨论】:

    • 谢谢。但是std::find_ifstd::equal_range 更像是std 的功能来隐藏例程代码,而不是容器的实现。正如你所说,这个特性破坏了容器的搜索优势。
    • @user14416 std::equal_range 可以,它具有对数复杂度。也许我会把它放在第一位。
    【解决方案3】:

    我会选择Boost Multi Index

    如果没有直接公开值,您甚至可以根据需要从 Property 定义键提取器和索引。 (为了便于打印,我将 Property 设为 struct 和 Object 公开,但只要 Property::get_value() 是公开的,这将起作用)

    #include <iostream>
    #include <string>
    #include <algorithm>
    
    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <boost/multi_index/sequenced_index.hpp>
    #include <boost/multi_index/identity.hpp>
    #include <boost/multi_index/member.hpp>
    #include <boost/optional.hpp>
    
    struct by_property_value{};
    struct byseq{};
    struct by_id{};
    
    struct Property{
        int value;
        std::string name;
    
        int get_value() const{
            return value;
        }
    };
    
    class Object {
    public:
       int id;
       Property property;
    };
    
    Object make_object(const int&id, const int& value, const std::string& name){
        Object res;
        res.id = id;
        Property p;
        p.value = value;
        p.name = name;
        res.property = p;
        return res;
    }
    
    bool operator<(const Object& lhs, const Object& rhs){
        return lhs.id < rhs.id;
    }
    
    
    
    using namespace boost;
    using namespace boost::multi_index;
    
    struct property_value_key_extractor{
        typedef int result_type;
    
        result_type operator()(const Object& o) const{
            return o.property.get_value();
        }
    };
    
    typedef multi_index_container<
        Object,
        indexed_by<
            sequenced<tag<byseq> >
            , ordered_unique<tag<by_id>,member<Object,int,&Object::id> >
            , ordered_non_unique<tag<by_property_value> , property_value_key_extractor >
        >
    > Objects;
    
    
    using namespace std;
    int main(int argc, char *argv[]) {
    
        Objects objects;
        objects.push_back(make_object(1,1000,"a"));
        objects.push_back(make_object(2,200,"b"));
        objects.push_back(make_object(3,50,"c"));
    
        typedef Objects::index<by_property_value>::type objects_by_property_value;
        typedef objects_by_property_value::iterator property_value_iterator;
    
        property_value_iterator it = objects.get<by_property_value>().find(200);
    
        if(it != objects.get<by_property_value>().end()){
            cout << it->id << " " << it->property.get_value() << " " << it->property.name << endl;
        }
    
        cout << "Print by propety value" << endl;
    
        for(property_value_iterator it = objects.get<by_property_value>().begin(); it != objects.get<by_property_value>().end(); it++ ){
            cout << it->id << " " << it->property.get_value() << " " << it->property.name << endl;
        }
    
    }
    

    输出:

    2200 b
    按属性值打印
    3 50 厘米
    2 200 b
    1 1000 个

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-13
      • 1970-01-01
      • 1970-01-01
      • 2013-01-29
      • 2017-02-03
      相关资源
      最近更新 更多