C ++中各种Custom Comparator函数之间的差异

时间:2016-07-17 15:31:59

标签: c++ sorting comparator

我发现有不同的方法为用户定义的对象定义自定义比较函数。我想知道在选择一个之前我应该​​考虑的事情。

如果我有学生对象,我可以通过以下方式编写自定义比较功能。

struct Student
{
    string name;
    uint32_t age;

    // Method 1: Using operator <
    bool operator<(const Student& ob)
    {
        return age < ob.age;
    }
};

// Method 2: Custom Compare Function
bool compStudent(const Student& a, const Student& b)
{
    return a.age < b.age;
}

// Method 3: Using operator ()
struct MyStudComp
{
    bool operator() (const Student& a, const Student& b)
    {
        return a.age < b.age;
    }
}obComp;

要对学生矢量进行排序,我可以使用以下任一方法。

vector<Student> studs; // Consider I have this object populated
std::sort(studs.begin(), studs.end());  // Method 1
std::sort(studs.begin(), studs.end(), compStudent);    // Method 2
std::sort(studs.begin(), studs.end(), obComp);  // Method 3

// Method 4: Using Lambda
sort(studs.begin(), studs.end(), 
     [](const Student& a, const Student& b) -> bool
     { 
        return a.age < b.age; 
     });

这些方法有何不同,我应该如何决定这些方法。提前谢谢。

4 个答案:

答案 0 :(得分:2)

不同方法之间的性能差别不大,但使用<可以让您更灵活,并且更容易使用内置插件。我也认为使用()有点奇怪。

您的示例中更大的问题是您的方法应该使用const引用而不是值。即bool operator<(Student ob)可以是friend bool operator<(const Student& ls, const Student& rs){...}。另外,请参阅here以了解重载运算符时要考虑的不同事项的一些示例。

答案 1 :(得分:0)

表现不会有明显的不同。但是在很多情况下很方便(并且预期)有一个/** * @ORM\ManyToMany(targetEntity="QuoteBundle\Entity\Quote", inversedBy="articles") * @ORM\JoinTable(name="quotes_articles") */ private $quotes; /** * Add devises * * @param \DevisBundle\Entity\Quote $quote * @return Article */ public function addQuote(\QuoteBundle\Entity\Quote $quote) { $this->quotes[] = $quote; return $this; } /** * Remove quotes * * @param \QuoteBundle\Entity\Quote $quote */ public function removeQuote(\QuoteBundle\Entity\Quote $quote) { $this->quotes->removeElement($quote); } /** * Get quotes * * @return \Doctrine\Common\Collections\Collection */ public function getQuotes() { return $this->quotes; } ,所以我会通过特殊比较功能来实现这一点。

答案 2 :(得分:0)

确实没有&#34;对&#34;方式本身,但如果你的对象有自定义比较器(即operator<等)是有意义的,那么简单地使用它们是明智的。但是,您可能希望基于不同的字段成员对对象进行排序,因此在这种情况下提供基于这些字段比较的自定义lambda是有意义的。

例如,您的Student班级目前使用重载operator<来比较学生年龄,因此如果您根据年龄对Student s的容器进行排序,则只需隐式使用此运算符。但是,您可能希望(在另一个时间)根据名称进行排序,因此在这种情况下,您可以提供自定义lambda作为最优雅的方法:

std::vector<Student> vec;
// populate vec
std::sort(vec.begin(), vec.end(), [](auto& lhs, auto& rhs) { return lhs.name < rhs.name; });

通过词典比较对学生姓名进行排序。

答案 3 :(得分:0)

  

这些方法有何不同,我应该如何决定这些方法。

他们隐含的意图陈述不同。你应该使用最简洁地表达你意图的表格。

依赖于operator<意味着有人在阅读您的代码时,您的对象隐式排序,如数字或字符串。它们应该是人们会说的东西,“很明显x在y之前出现”。

如果地图的排序更抽象,那么排序函数可能会更好,因为它表达了你在地图上强加订单的想法,这可能不是一个自然顺序。

在您给出的示例中,我可能会选择在名为ageIsLess的函数对象中表达意图。作为使用地图的代码阅读器现在完全了解意图。

例如:

#include <cstdint>
#include <set>
#include <string>
#include <algorithm>
#include <iterator>

struct Student
{
    std::string name;
    std::uint32_t age;

};

struct ByAscendingAge
{
    bool operator() (const Student& a, const Student& b) const
    {
        return a.age < b.age;
    }
};


bool age_is_less(const Student& l, const Student& r)
{
  return l.age < r.age;
};

bool name_is_less(const Student& l, const Student& r)
{
  return l.name < r.name;
};

int main()
{

  // this form expresses the intent that any 2 different maps of this type can have different ordering

  using students_by_free_function = std::set<Student, bool (*)(const Student&, const Student&)>;

  // ordered by age
  students_by_free_function by_age_1(age_is_less);

  // ordered by name
  students_by_free_function by_name_1(name_is_less);

  // above two maps are the same type so we can assign them, which implicitly reorders
  by_age_1 = by_name_1;


  // this form expresses the intent that the ordering is a PROPERTY OF THIS TYPE OF SET
  using students_by_age = std::set<Student, ByAscendingAge>;

  // note that we don't need a comparator in the constructor
  students_by_age by_age_2;

  // by_age_2 = by_age_1;  // not allowed because the sets are a different type

  // but we can assign iterator ranges of course
  std::copy(std::begin(by_age_1), 
            std::end(by_age_1), 
            std::inserter(by_age_2, 
                          std::end(by_age_2)));


}