这个std :: set初始化是如何工作的?

时间:2018-06-02 18:42:54

标签: c++ stdset

我在理解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的内容表明它们总是一样的,为什么会这样?

2 个答案:

答案 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_setunordered_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,最好让它返回。

相关问题