我在理解std :: set初始化的工作原理时遇到了一些麻烦。我在函数中有以下代码:
std::map<int, int> my_map = {
{16, 24},
{19, 29},
{15, 23},
{14, 22},
{13, 21},
{17, 28},
};
typedef std::function<bool(std::pair<int, int>, std::pair<int, int>)> comparefunction;
comparefunction compare =
[](std::pair<int, int> a, std::pair<int, int> b){
if(lessthan(a,b))
std::cout << "a" << std::endl;
else
std::cout << "b" << std::endl;
return true;
};
std::set<std::pair<int, int>, comparefunction>
values(my_map.begin(), my_map.end(), compare);
调用此功能时,它会打印几次“b”,为什么会这样?
编辑: 我意识到我使用了范围构造函数,但是如何使用map中的元素“自动”调用lambda函数?我似乎无法在文档中找到它。打印a和b的内容表明它们总是一样的,为什么会这样?
答案 0 :(得分:0)
我认为令人困惑的是地图迭代器的行为方式。迭代地图(从my_map.begin()
到my_map.end()
)意味着越过对,其类型为std::pair<K, V>
(对于键类型K
和映射值类型V
) ;在你的情况下它是std::pair<int, int>
。实际上,地图非常像这些对中的一组。
现在,你正在使用的集合的构造函数,如@AlgirdasPreidžius和@IgorTandetnik建议,是一个constructor which inserts all of the elements in some range(=从开始迭代器到结束迭代器)到集合中。所以你得到一组对。
最后,lambda可能会改变元素相同的规则。
现在有意义吗?
PS 1:您可能更喜欢unordered_set
和unordered_map
;给它一些但是看看哪种更适合你的需求。
答案 1 :(得分:0)
您正在使用某些值初始化集合。这些值恰好来自您的地图,但采购它们并不重要。重要的是set的构造函数将被赋予以下值以供使用(这些值实际上是std::pair
s,但括号表示法很方便):
{13,21} {14,22} {15,23} {16,24} {17,28} {19,29}
[此时,有人可能会注意到,而不是初始化地图的顺序,我按照第一个坐标(地图的键)按顺序排列。这是因为地图是有序的,因此begin()
到end()
遍历地图从最低密钥到最高密钥。]
那会发生什么? set的构造函数创建一个集合,然后将这些元素添加到它。第一个元素({13,21}
)没有问题;将元素添加到空集很容易。
但是,第二个元素需要放在维护顺序的位置。也就是说,构造函数需要知道新元素({14, 22}
)是否比现有元素({13,21}
)要小。怎么弄清楚?它调用你给它的函数:compare({14, 22}, {13,21})
。这返回true,因此新元素在集合中的旧元素之前
注意:参数的顺序不固定;因为输出是“b”,我猜这是使用的顺序。
添加第三个元素({15,23}
)是类似的。构造函数需要知道它在集合中的位置,因此它从一个现有元素开始(实现者选择我们从哪个开始)并使用该元素和新元素调用函数。您的函数返回true,因此新元素将放在现有元素之前。根据选择的元素,可能需要再次调用compare
来确定新元素在集合中的第一个。
注意:如果实现者选择了其他参数顺序,则新元素将在集合中排在最后;大概你会看到“a”而不是“b”s发生了这种情况。
剩下的三个元素等等。
想要混淆构造函数吗?让你的lambda返回false而不是true。这将使构造函数相信所有对都是等价的,这意味着在构造了六个元素之后,该集合将只包含一个({13,21}
)。在这种情况下,将“set”更改为“multiset”将允许添加其他元素。
实际上,构造函数可能会被总是返回true的函数混淆。无论a是否被认为低于b,最好让它返回。