如何在std :: set中查找具有特定字段值的对象?

时间:2011-08-12 15:37:25

标签: c++ stl find set

我调用一个返回std::set<T> const&的方法,其中T是类类型。我想要实现的是检查集合是否包含T类型的对象,其中包含自动测试中断言的特定字段值。应该对多个对象进行此检查。

这是一个简单的例子: 让类型TCar,因此示例set包含一堆汽车。现在我想找一辆具有特定颜色特定数量的车门的汽车,该车在该套装中具有特定的最高速度。如果找到 汽车,则第一个断言为真,并且应找到具有其他字段值的下一辆汽车。

我不允许更改T的实施。使用Boost就行了。

你会怎么做?

5 个答案:

答案 0 :(得分:16)

这取决于T的实施。让我们坚持你的班级Car的例子。假设该类看起来像这样:

class Car {
public:
    Car(std::string color, unsigned int number_of_doors,
        unsigned int top_speed);
    // getters for all these attributes
    // implementation of operator< as required for std::set
};

operator<应根据所有属性Car的实例进行排序,以便搜索所有可能的属性。否则你会得到不正确的结果。

基本上,你可以只使用这些属性来构建汽车实例。在这种情况下,您可以使用std::set::find并提供Car的临时实例,其中包含您要查找的属性:

car_set.find(Car("green", 4, 120));

如果您要搜索Car仅指定其属性子集的实例(例如所有绿色汽车),则可以将std::find_if与自定义谓词一起使用:

struct find_by_color {
    find_by_color(const std::string & color) : color(color) {}
    bool operator()(const Car & car) {
        return car.color == color;
    }
private:
    std::string color;
};

// in your code

std::set<Car>::iterator result = std::find_if(cars.begin(), cars.end(), 
                                              find_by_color("green"));
if(result != cars.end()) {
    // we found something
}
else {
    // no match
}

请注意,第二种解决方案具有线性复杂性,因为它不能依赖于您使用的谓词可能存在或不存在的任何排序。然而,第一个解决方案具有对数复杂度,因为它可以从std::set的顺序中受益。

如果@Betas对你的问题发表评论,你想在运行时编写谓词,你必须编写一些帮助类来组成不同的谓词。

答案 1 :(得分:1)

遇到这些问题时,我通常会坚持:

  • std::dequestd::vector辆汽车
  • 对于您要查看的每个属性,std::multiset指向汽车的指针,按属性值排序。

我小心地将这些容器隐藏在允许我插入汽车的类中。

查询界面可能会有所不同,但通常情况下,您可以利用multiset::equal_rangestd::sortstd::set_intersection

如果你想要一辆红色的沃尔沃汽车......你可以

  1. 通过equal_range所有红色汽车提取,给你一个迭代器对
  2. 通过equal_range所有沃尔沃汽车提取,给你一个迭代器对
  3. ...
  4. 按公共谓词(例如指针地址
  5. )对所有这些范围进行排序
  6. 重复申请set_intersectionstd::deque
  7. 另一种方法是将汽车存储到deque中,使用std::find枚举它们并选择满足所有属性的汽车。然而,这可能会有更糟糕的复杂性(这取决于你有多少红色汽车用于表示)

答案 2 :(得分:1)

你需要std::find_if,谓词函数对象检查你感兴趣的属性。它可能看起来像这样:

struct FindCar {
    FindCar(Colour colour, int doors, double top_speed) :
        colour(colour), doors(doors), top_speed(top_speed) {}

    bool operator()(Car const & car) const {
        return car.colour == colour
            && car.doors == doors
            && car.top_speed == top_speed;
    }

    Colour colour;
    int doors;
    double top_speed;
};

std::set<Car> const & cars = get_a_set_of_cars();
std::set<Car>::const_iterator my_car =
    std::find_if(cars.begin(), cars.end(), FindCar(Red, 5, 113));

在C ++ 0x中,您可以用lambda替换“FindCar”类,以使代码更短。

答案 3 :(得分:0)

您可以尝试重写operator&lt;(const T&amp; a,const T&amp; b)对这些属性强制执行命令,因此std :: set:find(const T&amp; x)将返回第一个不小于x的值。

EJ:

bool operator<(const Car& a, const Car& b)
{
    return a.color<b.color || (a.color==b.color && a.doors<b.doors) || (a.color==b.color && a.doors==b.doors && a.top_speed<b.top_speed);
}

std::set<Car> cars;
Car x;
cars.find(x);

答案 4 :(得分:0)

std :: set允许您提供自己的比较功能,如

template < class Key, class Compare = less<Key>,
       class Allocator = allocator<Key> > class set; 

仅比较您关心的值,它将表现为具有这些值的对象=。请注意,您可能需要一个多重集。

如果您真的关心登录性能,这将是这样做的方法。