C ++ STL - STL sort()的第三个参数如何工作?

时间:2013-11-10 09:04:36

标签: c++ sorting stl lambda

我希望根据其数据成员“class Person”对age的对象数组进行排序。我将对象存储在vector<Person> v

据我所知,至少有4种方法可以执行此操作,我根据下面列出的方法提出以下问题。

  1. operator()如何在课程中定义?我不应该重载'&lt;'这里的运营商呢?为什么'()'?

  2. 我在方法1中发送了一个对象作为第3个参数。但是,在方法2中,我发送了一个函数的名称。为什么会这样?

  3. 四种方法中哪一种最好?我觉得方法3是最简单的。

  4. 方法1

    class cmp
    {
    public:
        bool operator() (  Person const &a,  Person const &b )
        {
            return a.age < b.age ;
        }
    };
    
    sort( v.begin(), v.end(), cmp());
    

    方法2

    bool cmp( const Person a, const Person b ) 
    {
        return a.age < b.age ;
    }
    
    sort( v.begin(), v.end(), cmp );
    

    方法3

    bool operator < ( const Person a, const Person b )
    {
        return a.age < b.age ;
    }
    
    sort( v.begin(), v.end());
    

    方法4

    //using lambda expression
    sort( v.begin(), v.end(), [](const Person &a, const Person &b){return a.age < b.age;});
    

4 个答案:

答案 0 :(得分:4)

sort函数有两个重载

i。 void sort( RandomIt first, RandomIt last );它不接受比较功能,它希望项目定义operator<。你的方法3使用了这个重载。

template< class RandomIt >
void sort( RandomIt first, RandomIt last )
{
    ...

    if (*i < *j)
      ....

    ...
}

ii。 void sort( RandomIt first, RandomIt last, Compare comp );它接受比较功能,当您的商品未定义operator<时,此功能非常有用。

template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp )
{
    ...

    if (comp(*i, *j))
      ....

    ...
}

方法1,2,4使用此重载。所有传递的第三个参数都可以由()调用。方法1,通过cmp()发送一个对象,该对象重载operator(),上面的代码调用它。方法2和4,发送指向函数的指针,指向函数的指针可以由()调用。

答案 1 :(得分:4)

要使用std::sort(或任何相关函数)对范围进行排序,需要知道范围中的两个元素比较,以确定小于(或关系。

标准库函数std::sort两个版本:一个使用operator<,另一个使用 compare 函数/仿函数。您已在代码中使用了它们 - 特别是,示例中的第三个使用<,其余使用比较函数/仿函数。

哪一个是最佳方法?

嗯,这取决于。使用operator<的那个不太灵活,因为它已修复,但也需要您减少输入。当它足够时使用它。

另一个更灵活,因为您可以传递任何比较功能并相应地对您的元素进行排序。 operator<不够时使用它。此外,当您选择这种风格时,您还有其他选择:比较器可以是功能仿函数 lambda - 如果你使用函数或函子(在命名空间级别定义),那么你可以重用它们;另一方面,lambda 通常在函数作用域中定义,所以它不是可重用的,除非你在命名空间范围内定义它,在这种情况下它几乎相同作为功​​能。

示例,假设您要按递增顺序对int的向量进行排序:

 std::vector<int>  v{10, 3, 12, -26};
 std::sort(v.begin(), v.end());
 print(v);

输出:-26,3,10,12。所以operator<确实做到了。

但是如果你想要的元素仅仅考虑幅度(即忽略符号),那么你必须使用另一种风格:

 std::vector<int>  v{10, 3, 12, -26};
 auto abs_cmp = [](int a, int b) { return std::abs(a) < std::abs(b); };
 std::sort(v.begin(), v.end(), abs_cmp);
 print(v);

输出:3,10,12,-26。这是你在这种情况下所期望的输出。

希望有所帮助。

答案 2 :(得分:2)

  

如何在类中定义operator()?我不应该重载'&lt;'这里的运营商呢?为什么'()'?

operator()是函数调用运算符。类cmp的实例可以与函数可调用的方式相同的方式调用。 sort需要进行调用以执行必要的比较。

  

我在方法1中发送了一个对象作为第3个参数。但是,在方法2中,我发送了一个函数的名称。为什么会那样?

cmp的实例可以调用。函数名称可以调用。

  

四种方法中哪一种最好?我觉得方法3是最简单的。

3的主要缺点是你已经根据他们的年龄定义了一个人小于或大于另一个人。如果你想在其他地方按名称排序怎么办?您已经为Person定义了operator<,因此您无法再次执行相同的操作。所有其他三种方法都定义了在特定排序中使用的比较,而不是定义比较Person的一般含义。

2的主要缺点是编译器内联比1或4更难。它实际上没有超过1的优点,但为了完整性,sort可以获取函数指针很好

4的主要优点是,如果你只使用一次比较,那么在调用sort的同一行代码中将它放在那里通常更好,而不是在文件中的其他地方。

1的主要优点是它适用于C ++ 03(与4不同)。 4或多或少是1的新语法.1还具有使比较器可用于其他代码以使用相同名称的优点。如果你愿意的话,你也可以用lambda来实现(通过将lambda命名为auto变量来命名lambda。)

答案 3 :(得分:1)

  1. 您只需提供仿函数,具有operator()的对象,并在比较对象时使用。
  2. 据我所知,sort接受函子,函数和lambda表达式来使用它们来比较对象。
  3. 在方法3中,您不提供functor,function或lambda表达式进行比较,因此默认情况下使用标准运算符&lt;进行排序。显然,它不会按照您的意愿按年龄对Person对象进行排序。在我看来,最干净的形式是方法4,使用lambda表达式。