std :: set和<的奇怪行为运算符重载?

时间:2017-10-30 03:52:26

标签: c++ algorithm sorting c++11 set

我理解如果<运算符在C ++中重载(例如,将自定义结构插入std::set),则实现必须是基础类型的strict weak order。< / p>

考虑以下struct和实现。这个实现不是一个严格的弱顺序,但是代码编译并运行而没有抛出一个错误(我希望它会抛出一个错误,因为需要严格的弱顺序):

#include <iostream>
#include <set>
using namespace std;

struct Pixel {
    int x;
    int y;
};

bool operator < (Pixel lhs, Pixel rhs){
    return lhs.x < rhs.x || lhs.y < rhs.y;
};

int main(){
    set<Pixel> mySet;

    Pixel *newPixelA = new Pixel;
    newPixelA->x = 1;
    newPixelA->y = 3;

    Pixel *newPixelB = new Pixel;
    newPixelB->x = 4;
    newPixelB->y = 2;

    mySet.insert(*newPixelA);
    mySet.insert(*newPixelB);

}

这是预期的行为吗?编辑:使用Xcode。

3 个答案:

答案 0 :(得分:4)

编译器无法确定您的operator<是否是严格的弱排序。相反,std::set要求它的意思是,只有当你给它一个严格的弱排序时它才能正常工作。它不能保证如果你给别的东西会发生什么。

一般来说,C ++在需要某些东西时的含义是确保发生某些事情的 责任。如果这样做,那么编译器和库将保证您获得正确的结果。

答案 1 :(得分:1)

标准保证在满足比较器要求时的预期行为。否则,会发生什么取决于实现和数据集。您的比较函数可能适用于某些数据集(对于所有点,更大的x意味着更大的y)。 Set不能包含相等的元素(作为数学概念),而std::set等价意味着相等,所以如果已经有值a,它只会阻止你插入值b,例如的是:

a < b == true
b < a == true

即使a可能不等于b

答案 2 :(得分:1)

当比较运算符严格执行所包含元素的弱排序时,std::set中的对象按可预测的模式排序。如果没有,当您遍历对象时,无法确定std::set中首先出现哪个对象。

采用以下示例程序,其中Pixel1的排序未正确完成,Pixel2的排序正确。

#include <iostream>
#include <set>

struct Pixel1 {
    int x;
    int y;
};

bool operator < (Pixel1 lhs, Pixel1 rhs){
    return lhs.x < rhs.x || lhs.y < rhs.y;
};

struct Pixel2 {
    int x;
    int y;
};

bool operator < (Pixel2 lhs, Pixel2 rhs){
    if ( lhs.x != rhs.x )
    {
       return (lhs.x < rhs.x);
    }
    return (lhs.y < rhs.y);
};

template <typename Pixel> void print(std::set<Pixel> const& mySet)
{
   for ( Pixel p : mySet )
   {
      std::cout << "(" << p.x << ", " << p.y << ") ";
   }
   std::cout << std::endl;
}

template <typename Pixel> void test1()
{
   std::set<Pixel> mySet;

   Pixel pixelA = {2, 3};
   Pixel pixelB = {4, 2};
   Pixel pixelC = {4, 1};

   mySet.insert(pixelA);
   mySet.insert(pixelB);
   mySet.insert(pixelC);

   print(mySet);
}

template <typename Pixel> void test2()
{
   std::set<Pixel> mySet;

   Pixel pixelA = {2, 3};
   Pixel pixelB = {4, 2};
   Pixel pixelC = {4, 1};

   mySet.insert(pixelB);
   mySet.insert(pixelA);
   mySet.insert(pixelC);

   print(mySet);
}

int main()
{
   std::cout << "Pixel1 ... \n";
   test1<Pixel1>();
   test2<Pixel1>();

   std::cout << "Pixel2 ... \n";
   test1<Pixel2>();
   test2<Pixel2>();
}

输出

Pixel1 ... 
(4, 1) (4, 2) (2, 3) 
(4, 1) (2, 3) (4, 2) 
Pixel2 ... 
(2, 3) (4, 1) (4, 2) 
(2, 3) (4, 1) (4, 2) 

std::set<Pixel1>中对象的顺序取决于插入顺序,而std::set<Pixel2>中对象的顺序与插入顺序无关。

只有您可以判断您的申请是否可以接受