C++ Stl Set Behavior

C++ Stl Set Behavior

本文关键字:Behavior Set Stl C++      更新时间:2023-10-16

我试图运行下面的代码。我发现输出存在差异。我知道比较器功能中使用的排序机制存在问题。我基本上要找的是:1) Set 如何在内部存储数据。2) 如何解决此问题或将数据复制到其他 Set 的最佳方法。3)排序究竟是如何产生这个问题的。

#include <iostream>
#include <set>
using namespace std;
struct Comparator {
  bool operator()( const int& a, const int& b ) {
    if( a <= b ) 
      return true;
    else
      return false;
  }
};
int main()
{
  set< int, Comparator > customSet;
  for( unsigned k = 0, index = 2; k < 10; ++k ) {
    customSet.insert( index );
  }
  set< int, Comparator >::iterator iter = customSet.begin();
  for(; iter != customSet.end(); ++iter ) {
    cout<<*iter<<endl;
  }
  cout<<"---------------------------------"<<endl;
  set< int, Comparator > tempCustomSet ;//= customSet;
  tempCustomSet.insert( customSet.begin(), customSet.end() );
  iter = tempCustomSet.begin();
  for(; iter != tempCustomSet.end(); ++iter ) {
    cout<<*iter<<endl;
  }
  return 0;
}

1) Set 如何在内部存储数据

唯一的要求是元素是:

  • 根据比较器排序,因此如果Comp(a,b),则在迭代集合时a出现在b之前;
  • 独特,因此没有Comp(a,b)Comp(b,a)都成立的独特元素。

并且操作满足某些复杂性要求。

实际上,它们通常存储在二叉搜索树中;但这对用户来说并不重要。

2) 如何解决此问题或将数据复制到其他集合的最佳方法

为了满足要求,比较器必须是像<那样的严格弱排序,这样Comp(a,a)总是假的,而不是像<=那样的非严格排序。由于<是默认值,这意味着您根本不需要自定义比较器。

3)订单究竟是如何产生这个问题的

请注意,您的第一个循环是插入值2十次;我不确定这是否是意图。

给定所需的严格排序,insert(b)可能会通过查找第一个元素来寻找插入点a,以便Comp(a,b)为假;即b不应该在后面的第一个元素。然后,它将通过检查Comp(b,a)来检查唯一性。如果两者都为 false,则表示这两个值是等效的,因此不会插入b

由于您的比较并不严格,因此此唯一性测试可能会失败;因此您最终可能会得到重复的条目。或者可能会发生其他事情 - 行为未定义。

有关

std::set的更多详细信息,请参阅此参考。实现不应该与你有关(它们可能因平台而异),因为接口和复杂性保证对标准来说才是最重要的。典型的实现是使用红黑树。

你需要让你的Comparator使用operator<而不是operator<=。原因是,如果!Comparator(a, b) && !Comparator(b, a)计算结果为 truestd::set会认为元素等价(即两者都严格小于另一个)。

但是有了<=,你有a <= a等于true,所以!(a<=a) && !(a<=a)给相等元素false。而有了<,你a < a等于false所以!(a<a) && !(a<a) true.

做事的权利是:

struct Comparator 
{
    bool operator()(int const& lhs, int const& rhs) const 
    {
        return lhs < rhs; 
    }
};

这将保证相等的元素被视为等效的。请注意,有效STL中详细讨论了这个问题,"项目19.了解平等和等价之间的区别。

问题很可能是因为您的比较没有实现严格的弱排序。集合上的内部排序机制依赖于此。您可以通过将比较更改为小于:

struct Comparator {
  bool operator()( const int& a, const int& b ) const {
    return ( a < b ); 
  }
};

另一方面,默认情况下,std::set将使用此比较条件,因此您无需指定它。

在我对这个问题的回答中有一些相关信息(以及其他无数个SO问题)。

在两种情况下,您会得到不同的输出,因为您inserting in different ways .在情况 1 中,您将插入元素 2 十次。在这种情况下,当您在第一次插入整数 2 后,将调用您的 Comparator() 函数来决定插入的位置。在另一种情况下,您正在插入一个范围。在这里,调用函数接受第一个参数,即customSet.begin(),并将其与另一个参数 i,e customSet.end() 进行检查,如果这两个参数不相等,则只插入一个元素,否则不会插入元素。