【问题标题】:How to find an object with specific field values in a std::set?如何在 std::set 中查找具有特定字段值的对象?
【发布时间】:2011-08-12 15:37:24
【问题描述】:

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

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

我不允许更改T 的实现。使用 Boost 就可以了。

你会怎么做?

【问题讨论】:

  • 您想在运行时编写规则(红色 + 轿车 + 200kph)吗?还是硬编码足够好?
  • 硬编码规则就足够了。

标签: c++ stl find set


【解决方案1】:

这取决于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&lt; 应根据所有属性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 对您的问题的评论所建议的那样,您想在运行时组合谓词,则必须编写一些辅助类来组合不同的谓词。

【讨论】:

  • 字典顺序ftw。接得好。如果您想要更一般的查询,但这有限制(查找所有沃尔沃红色汽车)
  • 使用#include 获取std::find_if 例程。
【解决方案2】:

你会想要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”类以使代码更短。

【讨论】:

  • 这违背了std::set 的目的,对吧? std::set 能够使用二进制搜索在 O(log(n)) 中找到东西。如果您正在这样做,请尝试弄清楚如何使用 std::set::findstd::binary_search
【解决方案3】:

当遇到这样的问题时,我通常认为:

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

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

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

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

  1. equal_range 提取所有红色汽车,为您提供一对迭代器
  2. equal_range 提取所有沃尔沃汽车,为您提供一对迭代器
  3. ...
  4. 按通用谓词对所有这些范围进行排序,例如按指针地址
  5. set_intersection 反复应用到std::deque

另一种方法是将汽车存储到deque 中,枚举它们并使用std::find 选择满足您所有属性的汽车。然而,这可能更复杂(这取决于你有多少辆红色汽车)

【讨论】:

    【解决方案4】:

    您可以尝试覆盖 operator

    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);
    

    【讨论】:

      【解决方案5】:

      std::set 允许您提供自己的比较函数,如

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

      只比较你关心的值,它会表现得好像具有这些值的对象是 =。请注意,您可能需要一个多重集。

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

      【讨论】:

        猜你喜欢
        • 2012-07-13
        • 2019-07-19
        • 2018-08-05
        • 2021-10-01
        • 2015-09-03
        • 2016-07-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多