【问题标题】:Differences between various Custom Comparator functions in C++C++中各种自定义比较器函数的区别
【发布时间】:2016-07-17 15:31:58
【问题描述】:

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

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

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; 
     });

这些方法有什么不同,我应该如何在它们之间做出决定。提前致谢。

【问题讨论】:

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

标签: c++ sorting comparator


【解决方案1】:

不同方法之间的性能差别不大,但是,使用&lt; 会让您更加灵活,并且使使用内置函数更加容易。我也认为使用() 有点奇怪。

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

【讨论】:

  • 感谢 const 参考建议。
【解决方案2】:

性能不会有明显不同。但在许多情况下,拥有operator&lt; 会很方便(并且是意料之中的),所以我会通过特殊的比较功能来实现这一点。

【讨论】:

    【解决方案3】:

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

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

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

    学生姓名通过字典比较排序。

    【讨论】:

      【解决方案4】:

      这些方法有什么不同,我应该如何在它们之间做出决定。

      它们的隐含意图陈述不同。您应该使用最简洁地表达您的意图的形式。

      依赖operator&lt; 暗示阅读您的代码的人您的对象是隐式排序的,例如数字或字符串。它们应该是人们会说的东西,“显然 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)));
      
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多