您可以将函数指针,函数对象(或boost lambda)传递给std :: sort,以定义要排序的容器元素的严格弱排序。
然而,有时候(足够我多次这样做了),你希望能够进行“原始”比较。
如果您正在排序表示联系人数据的对象集合,那么这将是一个简单的例子。有时你会想要按
last name, first name, area code排序。其他时间
first name, last name- 还有其他时间
age, first name, area code...等
现在,您当然可以为每个案例编写一个额外的函数对象,但这违反了DRY原则 - 特别是如果每个比较都不那么重要。
看起来您应该能够编写比较函数的层次结构 - 低级别的比较函数执行单一,原始的比较(例如,名字<名字),然后更高级别的函数连续调用低级别的函数(可能用&&&来利用短路评估)来生成复合函数。
这种方法的问题在于std :: sort采用二元谓词 - 谓词只能返回一个bool。因此,如果您正在编写它们,则无法判断“假”是表示相等还是大于。你可以让你的低级谓词返回一个int,有三个状态 - 但是你必须先将它们包装在更高级别的谓词中,然后再将它们与std :: sort一起使用。
总之,这些并非不可克服的问题。它似乎比应该更难 - 并且肯定会邀请帮助程序库实现。
因此,有没有人知道任何预先存在的库(特别是如果它是std或boost库)可以在这里提供帮助 - 对此事有任何其他想法吗?
[更新]
正如一些评论中提到的那样 - 我已经开始编写自己的类实现来管理它。它相当小,并且可能在一般情况下存在一些问题。但在此基础上,对于任何有兴趣的人,课程都在这里:
这里有一些辅助函数(以避免指定模板args):
答案 0 :(得分:8)
你可以建立一个像这样的小链系统:
struct Type {
string first, last;
int age;
};
struct CmpFirst {
bool operator () (const Type& lhs, const Type& rhs) { return lhs.first < rhs.first; }
};
struct CmpLast {
bool operator () (const Type& lhs, const Type& rhs) { return lhs.last < rhs.last; }
};
struct CmpAge {
bool operator () (const Type& lhs, const Type& rhs) { return lhs.age < rhs.age; }
};
template <typename First, typename Second>
struct Chain {
Chain(const First& f_, const Second& s_): f(f_), s(s_) {}
bool operator () (const Type& lhs, const Type& rhs) {
if(f(lhs, rhs))
return true;
if(f(rhs, lhs))
return false;
return s(lhs, rhs);
}
template <typename Next>
Chain <Chain, Next> chain(const Next& next) const {
return Chain <Chain, Next> (*this, next);
}
First f;
Second s;
};
struct False { bool operator() (const Type& lhs, const Type& rhs) { return false; } };
template <typename Op>
Chain <False, Op> make_chain(const Op& op) { return Chain <False, Op> (False(), op); }
然后使用它:
vector <Type> v; // fill this baby up
sort(v.begin(), v.end(), make_chain(CmpLast()).chain(CmpFirst()).chain(CmpAge()));
最后一行有点冗长,但我认为很明显是什么意思。
答案 1 :(得分:2)
处理此问题的一种常规方法是对多个传递进行排序并使用稳定排序。请注意,std::sort
通常不稳定。但是,有std::stable_sort
。
那就是说,我会写一个包装函数的包装器,它返回一个三态(代表更少,等于,更大)。
答案 2 :(得分:2)
std::sort
不能保证稳定,因为稳定排序通常比非稳定排序慢...所以多次使用稳定排序看起来像是性能问题的处方......
是的,真的很遗憾那种要求谓词: 除了创建一个接受三态函数向量的仿函数之外,我没有别的办法......
答案 3 :(得分:2)
你可以试试这个:
用法:
struct Citizen {
std::wstring iFirstName;
std::wstring iLastName;
};
ChainComparer<Citizen> cmp;
cmp.Chain<std::less>( boost::bind( &Citizen::iLastName, _1 ) );
cmp.Chain<std::less>( boost::bind( &Citizen::iFirstName, _1 ) );
std::vector<Citizen> vec;
std::sort( vec.begin(), vec.end(), cmp );
实现:
template <typename T>
class ChainComparer {
public:
typedef boost::function<bool(const T&, const T&)> TComparator;
typedef TComparator EqualComparator;
typedef TComparator CustomComparator;
template <template <typename> class TComparer, typename TValueGetter>
void Chain( const TValueGetter& getter ) {
iComparers.push_back( std::make_pair(
boost::bind( getter, _1 ) == boost::bind( getter, _2 ),
boost::bind( TComparer<TValueGetter::result_type>(), boost::bind( getter, _1 ), boost::bind( getter, _2 ) )
) );
}
bool operator()( const T& lhs, const T& rhs ) {
BOOST_FOREACH( const auto& comparer, iComparers ) {
if( !comparer.first( lhs, rhs ) ) {
return comparer.second( lhs, rhs );
}
}
return false;
}
private:
std::vector<std::pair<EqualComparator, CustomComparator>> iComparers;
};
答案 4 :(得分:1)
链接解决方案很冗长。您还可以将boost :: bind与std :: logical_and结合使用来构建排序谓词。有关详细信息,请参阅链接的文章:How the boost bind library can improve your C++ programs
答案 5 :(得分:1)
C ++ 11中的可变参数模板提供了更短的选项:
#include <iostream>
using namespace std;
struct vec { int x,y,z; };
struct CmpX {
bool operator() (const vec& lhs, const vec& rhs) const
{ return lhs.x < rhs.x; }
};
struct CmpY {
bool operator() (const vec& lhs, const vec& rhs) const
{ return lhs.y < rhs.y; }
};
struct CmpZ {
bool operator() (const vec& lhs, const vec& rhs) const
{ return lhs.z < rhs.z; }
};
template <typename T>
bool chained(const T &, const T &) {
return false;
}
template <typename CMP, typename T, typename ...P>
bool chained(const T &t1, const T &t2, const CMP &c, P...p) {
if (c(t1,t2)) { return true; }
if (c(t2,t1)) { return false; }
else { return chained(t1, t2, p...); }
}
int main(int argc, char **argv) {
vec x = { 1,2,3 }, y = { 2,2,3 }, z = { 1,3,3 };
cout << chained(x,x,CmpX(),CmpY(),CmpZ()) << endl;
return 0;
}