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));
}
}
也许有更好的方法按时间对事件进行排序?显然,我可以做一个向量并循环查找最大的事件,但只是认为使用内置槽作为比较器会很好。
答案 0 :(得分:3)
如果std::set
中元素的相对顺序发生变化,则结果是未定义的行为。
在你的情况下,只要事情永远不会发生,时间永远不会溢出,-now
毫无意义。如果在任何时候当前时间之前和之后发生事情,您的代码将表现出未定义的行为。
如果您只是按时间对所有内容进行排序,然后现在查找 附近的元素,并从那里进行处理,您将获得类似的结果,但没有未定义的行为。
即,有int64_t
到Event*
的地图。将时间戳存储在int64_t
中(这使一些事情变得更容易)。然后获取当前时间的lower_bound
,并仔细检查它,找到与现在相近的“最近”(或您选择的任何窗口)。
如果您的编译器支持透明比较器(C ++ 14功能),则可以使用std::set
执行此操作。只需在Event*
和int64_t
时间戳之间实现透明比较器。
答案 1 :(得分:2)
如果我弄错了,C ++大师会纠正我:
我相信std::set
实例使用的比较器会对基础容器中集合中包含的元素进行排序。这对于最小化std::set
定义的操作和函数的复杂性是必要的。由于集合依赖于这种排序,因此更改的顺序非常不明智 - 它会导致间歇性(有时可能仍然是正确的顺序),集合函数中的未定义行为依赖于集合元素的正确排序
在没有看到您的代码的情况下猜测,我会说std::list
或std::vector
可能更适合您的应用。检查您将使用的操作和功能的复杂性,并确保所有这些操作和功能都存在于所选容器中。您可能需要回到std::algorithm
课程以获得某些任务的帮助,但如果可能的话,尽量坚持使用容器的内置功能。</ p>
答案 2 :(得分:2)
我普遍同意Yakk和Conduit所说的:不要让你的比较器严重依赖全球状态。为了更有力地支持这一点,Herb Sutter和Andrei Alexandrescu在 C ++编码标准中的第87项说:
使谓词成为纯函数。
谓词纯度:谓词是一个返回是/否答案的函数对象,通常为
bool
值。如果函数的结果仅依赖于它的参数,那么函数在数学意义上是纯粹的(请注意,这种“纯”的使用与纯虚函数无关。)不允许谓词持有或访问影响其
operator()
结果的状态,包括成员和全局状态。希望为谓词设置operator()
const
成员函数。
我最重要的是要添加到讨论中,这是一个简单的解决方案,可以实现我认为应该首选的多个排序。
Foo
类型)存储在std::vector<Foo>
中。显而易见的选择当然是push_back()
它们出现时。{/ li>
Compare
和相应的bool operator()(const Foo *, const Foo *) const;
(使用接受两个迭代器和比较器的构造函数)定义纯排序仿函数std::set<Foo *, Compare>
,指向矢量的指针的轻量级结构。如果您打算访问每个排序倍数(按元素数量的顺序)次数,这种方法是值得的。如果您只想查找少量元素以进行各种排序,请考虑使用std::partial_sort
或std::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]