C ++ std :: set使用基于时间的比较器?

时间:2015-01-07 21:23:33

标签: c++ comparator

C ++ std :: set中使用的比较器是否依赖于时间?即一次返回一个东西的比较器,但即使对于完全相同的物体,也可能在不同的时间返回另一个结果?

我已经读过std :: sets与给定的比较器的排序很弱。 也许有一种方法可以在获得mySet.begin()myset.end()值之前刷新订单?

作为可能随时间变化的比较器的示例,请考虑事件并希望优先处理更接近当前时间的事件:

class Event {
public:
    int64_t nTime;
}

class EventComparator {
    // pa < pb
    bool operator()(Event* pa, Event* pb) {
        int64_t now = GetTime();
        return (std::abs(pa->nTime - now) > std::abs(pb->nTime - now));
    }
}

也许有更好的方法按时间对事件进行排序?显然,我可以做一个向量并循环查找最大的事件,但只是认为使用内置槽作为比较器会很好。

3 个答案:

答案 0 :(得分:3)

如果std::set中元素的相对顺序发生变化,则结果是未定义的行为。

在你的情况下,只要事情永远不会发生,时间永远不会溢出,-now毫无意义。如果在任何时候当前时间之前和之后发生事情,您的代码将表现出未定义的行为。

如果您只是按时间对所有内容进行排序,然后现在查找 附近的元素,并从那里进行处理,您将获得类似的结果,但没有未定义的行为。

即,有int64_tEvent*的地图。将时间戳存储在int64_t中(这使一些事情变得更容易)。然后获取当前时间的lower_bound,并仔细检查它,找到与现在相近的“最近”(或您选择的任何窗口)。

如果您的编译器支持透明比较器(C ++ 14功能),则可以使用std::set执行此操作。只需在Event*int64_t时间戳之间实现透明比较器。

答案 1 :(得分:2)

如果我弄错了,C ++大师会纠正我:

我相信std::set实例使用的比较器会对基础容器中集合中包含的元素进行排序。这对于最小化std::set定义的操作和函数的复杂性是必要的。由于集合依赖于这种排序,因此更改的顺序非常不明智 - 它会导致间歇性(有时可能仍然是正确的顺序),集合函数中的未定义行为依赖于集合元素的正确排序

在没有看到您的代码的情况下猜测,我会说std::liststd::vector可能更适合您的应用。检查您将使用的操作和功能的复杂性,并确保所有这些操作和功能都存在于所选容器中。您可能需要回到std::algorithm课程以获得某些任务的帮助,但如果可能的话,尽量坚持使用容器的内置功能。<​​/ p>

答案 2 :(得分:2)

我普遍同意YakkConduit所说的:不要让你的比较器严重依赖全球状态。为了更有力地支持这一点,Herb Sutter和Andrei Alexandrescu在 C ++编码标准中的第87项说:

  

使谓词成为纯函数。

     

谓词纯度:谓词是一个返回是/否答案的函数对象,通常为bool值。如果函数的结果仅依赖于它的参数,那么函数在数学意义上是纯粹的(请注意,这种“纯”的使用与纯虚函数无关。)

     

不允许谓词持有或访问影响其operator()结果的状态,包括成员和全局状态。希望为谓词设置operator() const成员函数。

我最重要的是要添加到讨论中,这是一个简单的解决方案,可以实现我认为应该首选的多个排序。

  1. 使用永不改变的任意顺序将所有元素(Foo类型)存储在std::vector<Foo>中。显而易见的选择当然是push_back()它们出现时。{/ li>
  2. 对于您希望使用的每个排序,使用Compare和相应的bool operator()(const Foo *, const Foo *) const;(使用接受两个迭代器和比较器的构造函数)定义纯排序仿函数std::set<Foo *, Compare>,指向矢量的指针的轻量级结构。
  3. 如果订购过时,只需处置相应的设置。
  4. 如果您打算访问每个排序倍数(按元素数量的顺序)次数,这种方法是值得的。如果您只想查找少量元素以进行各种排序,请考虑使用std::partial_sortstd::partial_sort_copy。考虑对要复制的便宜指针std::vector<Foo *>进行排序,而不是原始Foo对象。

    现在有些代码:

    #include <algorithm>
    #include <cmath>
    #include <iomanip>
    #include <iostream>
    #include <set>
    #include <string>
    #include <utility>
    #include <vector>
    
    using Foo = std::pair<float, float>;
    using FooIter = std::vector<Foo>::iterator;
    
    std::ostream&
    operator<<(std::ostream& os, const Foo& foo)
    {
      os << std::fixed << std::setprecision(4)
         << "["
         << std::setw(8) << foo.first
         << ", "
         << std::setw(8) << foo.second
         << "]";
      return os;
    }
    
    // Ugly template, irrelevant for this discussin, don't use in production code.
    template<typename ContainerT>
    void
    print_container(const ContainerT& container, const std::string& name)
    {
      std::cout << name << "\n\n";
      for (const auto& it : container)
        std::cout << *it << "\n";
      std::cout << std::endl;
    }
    
    int
    main()
    {
      // Create a vector of 10 'Foo's.
      std::vector<Foo> items {};
      for (float x = 0.0f; x < 10.0f; x += 1.0f)
        items.emplace_back(x, std::sin(x));
      // For convenience, also create a vector of iterators into that vector.
      std::vector<FooIter> item_iterators {};
      for (auto it = items.begin(); it != items.end(); ++it)
        item_iterators.push_back(it);
      // Comparator based on the first element.
      const auto cmp1 = [](const FooIter it1, const FooIter it2)->bool{
        return it1->first < it2->first;
      };
      // Comparator based on the second element.
      const auto cmp2 = [](const FooIter it1, const FooIter it2)->bool{
        return it1->second < it2->second;
      };
      {
        // Create a set ordered by the value of the first element.
        std::set<FooIter, decltype(cmp1)> set1 {
          item_iterators.begin(), item_iterators.end(), cmp1
        };
        print_container(set1, "set1");
      }
      {
        // Create a set ordered by the value of the second element.
        std::set<FooIter, decltype(cmp2)> set2 {
          item_iterators.begin(), item_iterators.end(), cmp2
        };
        print_container(set2, "set2");
      }
      {
        // Create a vector of the three smallest (by the first element) values.
        std::vector<FooIter> vec1(3);
        std::partial_sort_copy(item_iterators.begin(), item_iterators.end(),
                               vec1.begin(), vec1.end(), cmp1);
        print_container(vec1, "vec1");
      }
      {
        // Create a vector of the three smallest (by the second element) values.
        std::vector<FooIter> vec2(3);
        std::partial_sort_copy(item_iterators.begin(), item_iterators.end(),
                               vec2.begin(), vec2.end(), cmp2);
        print_container(vec2, "vec2");
      }
      return 0;
    }
    

    输出结果为:

    set1
    
    [  0.0000,   0.0000]
    [  1.0000,   0.8415]
    [  2.0000,   0.9093]
    [  3.0000,   0.1411]
    [  4.0000,  -0.7568]
    [  5.0000,  -0.9589]
    [  6.0000,  -0.2794]
    [  7.0000,   0.6570]
    [  8.0000,   0.9894]
    [  9.0000,   0.4121]
    
    set2
    
    [  5.0000,  -0.9589]
    [  4.0000,  -0.7568]
    [  6.0000,  -0.2794]
    [  0.0000,   0.0000]
    [  3.0000,   0.1411]
    [  9.0000,   0.4121]
    [  7.0000,   0.6570]
    [  1.0000,   0.8415]
    [  2.0000,   0.9093]
    [  8.0000,   0.9894]
    
    vec1
    
    [  0.0000,   0.0000]
    [  1.0000,   0.8415]
    [  2.0000,   0.9093]
    
    vec2
    
    [  5.0000,  -0.9589]
    [  4.0000,  -0.7568]
    [  6.0000,  -0.2794]