我发现有不同的方法为用户定义的对象定义自定义比较函数。我想知道在选择一个之前我应该考虑的事情。
如果我有学生对象,我可以通过以下方式编写自定义比较功能。
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;
});
这些方法有何不同,我应该如何决定这些方法。提前谢谢。
答案 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)));
}