Using custom std::set comparator
我正在尝试将一组整数中的项目的默认顺序更改为字典式而不是数字式,并且无法使用g ++进行编译:
file.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | bool lex_compare(const int64_t &a, const int64_t &b) { stringstream s1,s2; s1 << a; s2 << b; return s1.str() < s2.str(); } void foo() { set<int64_t, lex_compare> s; s.insert(1); ... } |
我收到以下错误:
1 2 | error: type/value mismatch at argument 2 in template parameter list for ‘template<class _Key, class _Compare, class _Alloc> class std::set’ error: expected a type, got ‘lex_compare’ |
我究竟做错了什么?
您正在使用的函数应使用函子(重载()运算符的类,因此可以像函数一样调用它)。
1 2 3 4 5 6 7 8 | struct lex_compare { bool operator() (const int64_t& lhs, const int64_t& rhs) const { stringstream s1, s2; s1 << lhs; s2 << rhs; return s1.str() < s2.str(); } }; |
然后,您将类名称用作类型参数
1 | set<int64_t, lex_compare> s; |
如果要避免仿函数样板代码,也可以使用函数指针(假设
1 | set<int64_t, bool(*)(const int64_t& lhs, const int64_t& rhs)> s(&lex_compare); |
1.现代C ++ 11解决方案
1 2 | auto cmp = [](int a, int b) { return ... }; std::set<int, decltype(cmp)> s(cmp); |
我们使用lambda函数作为比较器。与往常一样,比较器应返回布尔值,指示是否以其定义的特定严格弱顺序将作为第一个参数传递的元素视为在第二个参数之前。
在线演示
2.与第一个解决方案相似,但功能代替了lambda
使比较器像通常的布尔函数一样
1 2 3 | bool cmp(int a, int b) { return ...; } |
然后用
1 | std::set<int, decltype(&cmp)> s(&cmp); |
在线演示
3.使用带有
1 2 3 4 5 6 7 8 9 | struct cmp { bool operator() (int a, int b) const { return ... } }; // ... // later std::set<int, cmp> s; |
在线演示
4.替代解决方案:从布尔函数创建结构
采取布尔函数
1 2 3 | bool cmp(int a, int b) { return ...; } |
并使用
1 2 | #include <type_traits> using Cmp = std::integral_constant<decltype(&cmp), &cmp>; |
最后,使用struct作为比较器
1 | std::set<X, Cmp> set; |
在线演示
Yacoby的答案激发了我编写一个用于封装函子样板的适配器的信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | template< class T, bool (*comp)( T const &, T const & ) > class set_funcomp { struct ftor { bool operator()( T const &l, T const &r ) { return comp( l, r ); } }; public: typedef std::set< T, ftor > t; }; // usage bool my_comparison( foo const &l, foo const &r ); set_funcomp< foo, my_comparison >::t boo; // just the way you want it! |
哇,我觉得值得这么做!
您可以使用函数比较器而不必像这样包装它:
1 2 3 4 5 6 | bool comparator(const MyType &lhs, const MyType &rhs) { return [...]; } std::set<MyType, bool(*)(const MyType&, const MyType&)> mySet(&comparator); |
每当您需要该类型的集合时,这很烦人,并且如果不使用相同的比较器来创建所有集合,可能会引起问题。
如果要处理定义了
如http://en.cppreference.com/w/cpp/container/set/find所述,C ++ 14添加了两个新的
1 2 | template< class K > iterator find( const K& x ); template< class K > const_iterator find( const K& x ) const; |
这使您可以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include <cassert> #include <set> class Point { public: // Note that there is _no_ conversion constructor, // everything is done at the template level without // intermediate object creation. //Point(int x) : x(x) {} Point(int x, int y) : x(x), y(y) {} int x; int y; }; bool operator<(const Point& c, int x) { return c.x < x; } bool operator<(int x, const Point& c) { return x < c.x; } bool operator<(const Point& c, const Point& d) { return c.x < d; } int main() { std::set<Point, std::less<>> s; s.insert(Point(1, -1)); s.insert(Point(2, -2)); s.insert(Point(0, 0)); s.insert(Point(3, -3)); assert(s.find(0)->y == 0); assert(s.find(1)->y == -1); assert(s.find(2)->y == -2); assert(s.find(3)->y == -3); // Ignore 1234, find 1. assert(s.find(Point(1, 1234))->y == -1); } |
在Ubuntu 16.10,
1 2 | g++ -std=c++14 -Wall -Wextra -pedantic -o main.out main.cpp ./main.out |
有关