stable_sort user defined comparison function stepping pit (shallow understanding)

Keywords: C++ Algorithm STL

1. General

"A good memory is better than a bad pen". This article is the first article of "difficult and miscellaneous diseases encountered". This article mainly introduces the STL stable encountered in today's work_ The problem of custom comparison function of sort algorithm is only a superficial introduction. The specific explanation will be explained after learning the STL source code (the great treasure of STL is only at the level of use, and it has not been used well).

2. Problem description

When a bug is encountered at work, the general situation can be expressed by the following code:

#include <iostream>
#include <algorithm>
#include <vector>

struct Student
{
    int id;
    int num;
};

void printStuId(const Student& stu1, const Student& stu2)
{
    std::cout << stu1.id << ", " << stu2.id << std::endl;
}

int main()
{
    std::vector<Student> stus{{1, 2}, {2, 2}};
    std::stable_sort(stus.begin(), stus.end(),
        [](const Student& stu1, const Student& stu2)
        {
            printStuId(stu1, stu2);
            return stu1.num <= stu2.num;
        }
    );

    std::cout << stus.at(0).id << std::endl;

    return 0;
}

My requirement is to input two Student objects and sort them in ascending order according to the num of students. The output result is the id value of the first Student object after sorting. As shown in the code, the comparison rules are implemented by lambda function. Do you have any problems?
In fact, the problem is very big:

  1. The output of printStuId(stu1, stu2) function is "2, 1"
  2. stus.at(0).id turned out to be 2

What the hell is this? There is no logic at all. It splits on the spot, ha ha ha. Question 1 is no problem, but I don't know. The code of question 2 ran for two years without problem. It was strange and helpless.

3. Problem solving

3.1 solution of problem 1

Read STL stable_ The source code of sort is explained as follows:

/*
*  Sorts the elements in the range @p [__first,__last) in ascending order,
*  such that for each iterator @p i in the range @p [__first,__last-1),
*  @p __comp(*(i+1),*i) is false.
*
*/

You see, is it different from your previous understanding? My own understanding has always been "__ comp(*i, *(i+1)) is true ", please forgive my ignorance. The design of C + + is always inconsistent with normal thinking, but there must be some reasons. I will explain it after I study it.

3.2 problem solving

The code in question 2 ran for two years without problem. Why did it suddenly go wrong? It is because there is a scene where two Student objects num are equal, that is, the comparison rules are equal.
By looking up the data (references 1, 2 and 3), the explanation is as follows:

/*
*  Binary function that accepts two elements in the range as arguments,
*  and returns a value convertible to bool. The value returned indicates
*  whether the element passed as first argument is considered to go before
*  the second in the specific strict weak ordering it defines.
*/

The concept of "Strict Weak Ordering" is mentioned, which requires the following conditions to be met:

  1. Reflexivity: comp(x, x) must be false
  2. Asymmetry: if comp(x, y) and comp(y, x), the result must be opposite
  3. Transitivity: if comp(x, y) is true and comp (y, z) is true, then comp(x, z) must be true

The custom comp of the above code violates 1 / 2, so it is stable_ There was a problem using sort. In addition, according to the explanation in question 1, it can also be concluded that the order of Student objects has changed. But I use stable_sort sort, for equal elements, the relative position should remain the same, which made me confused for a time.
The solution is to "return stu1. Num < = stu2. Num;" Modify to "return stu1. Num < stu2. Num;".

4. Summary

The comp functions provided by STL functions must meet Strict Weak Ordering, which shows how important it is to understand basic concepts“ We should not only know its nature, but also know its reason ". Next, we will continue to study the data structure, algorithm and STL source code. Stay hungry, stay foolish!

5. References

  1. cplusplus, https://www.cplusplus.com/reference/algorithm/stable_sort/
  2. Clause 21 of Effective STL: always let the comparison function return false for equal values
  3. SGI-STL, http://www.sgi.com/tech/stl/StrictWeakOrdering.html

Welcome to criticize, comment and reprint (please indicate the source). Thank you!

Posted by Lokolo on Wed, 01 Sep 2021 22:26:07 -0700