使用自定义std :: set比较器

Omr*_*dan 84 c++ stl

我试图将一组整数中的项的默认顺序更改为lexicographic而不是numeric,并且我无法使用g ++进行以下编译:

file.cpp:

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);
    ...
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

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’
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

Yac*_*oby 136

您正在使用一个函数,因为您应该使用仿函数(一个重载()运算符的类,因此可以像函数一样调用它.

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();
    }
};
Run Code Online (Sandbox Code Playgroud)

然后使用类名作为类型参数

set<int64_t, lex_compare> s;
Run Code Online (Sandbox Code Playgroud)

如果你想避免仿函数样板代码,你也可以使用函数指针(假设lex_compare是一个函数).

set<int64_t, bool(*)(const int64_t& lhs, const int64_t& rhs)> s(&lex_compare);
Run Code Online (Sandbox Code Playgroud)

  • @Omry:我有兴趣知道你正在使用什么编译器:http://codepad.org/IprafuVf (4认同)
  • 我们可以使用decltype(lex_compare)来表示函数类型吗? (4认同)
  • @Omry C++标准说第二个模板参数必须是类型的名称 - 函数名称不是类型的名称. (3认同)
  • @LewisChan正确的术语是`std :: set <int64_t,decltype(&lex_compare)> s(&lex_compare)` (2认同)

dir*_*lik 58

1.现代C++ 11解决方案

auto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> s(cmp);
Run Code Online (Sandbox Code Playgroud)

我们使用lambda函数作为比较器.像往常一样,比较器应该返回布尔值,指示作为第一个参数传递的元素是否被认为是在它定义的特定严格弱顺序中的第二个之前.

在线演示

2.与第一种解决方案类似,但功能代替lambda

使比较器成为通常的布尔函数

bool cmp(int a, int b) {
    return ...;
}
Run Code Online (Sandbox Code Playgroud)

然后使用它

std::set<int, decltype(&cmp)> s(&cmp);
Run Code Online (Sandbox Code Playgroud)

在线演示

3.使用struct with ()operator的旧解决方案

struct cmp {
    bool operator() (int a, int b) const {
        return ...
    }
};

// ...
// later
std::set<int, cmp> s;
Run Code Online (Sandbox Code Playgroud)

在线演示

4.替代解决方案:从布尔函数创建struct

采取布尔函数

bool cmp(int a, int b) {
    return ...;
}
Run Code Online (Sandbox Code Playgroud)

并使用它来构建struct std::integral_constant

#include <type_traits>
using Cmp = std::integral_constant<decltype(&cmp), &cmp>;
Run Code Online (Sandbox Code Playgroud)

最后,使用struct作为比较器

std::set<X, Cmp> set;
Run Code Online (Sandbox Code Playgroud)

在线演示

  • 在示例1中,cmp需要传递到构造函数中吗?当 lambda 类型作为模板类型给出时,集合会构造自己吗? (4认同)
  • @PeteUK 在 C++20 比较器之前必须传递到构造函数中。在 C++20 中,可以使用不带参数的构造函数。谢谢你的提问;答案已更新 (3认同)
  • 那个5.太疯狂了。人们每天都会发现这门语言的新角落和缝隙。 (3认同)

Pot*_*ter 17

Yacoby的回答激励我编写一个用于封装仿函数样板的适配器.

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!
Run Code Online (Sandbox Code Playgroud)

哇,我觉得那值得一试!

  • 我猜是一个意见问题. (16认同)

Tom*_*ock 6

您可以使用函数比较器而不包装它,如下所示:

bool comparator(const MyType &lhs, const MyType &rhs)
{
    return [...];
}

std::set<MyType, bool(*)(const MyType&, const MyType&)> mySet(&comparator);
Run Code Online (Sandbox Code Playgroud)

每次需要一组该类型时输入会很烦人,如果不使用相同的比较器创建所有集合,则会导致问题.


Cir*_*四事件 5

std::less<> 使用自定义类时 operator<

如果您正在处理一组已operator<定义的自定义类,那么您只需使用std::less<>.

http://en.cppreference.com/w/cpp/container/set/find所述,C++14 添加了两个新findAPI:

template< class K > iterator find( const K& x );
template< class K > const_iterator find( const K& x ) const;
Run Code Online (Sandbox Code Playgroud)

这允许您执行以下操作:

主程序

#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);
}
Run Code Online (Sandbox Code Playgroud)

编译并运行:

g++ -std=c++14 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请std::less<>访问:什么是透明比较器?

在 Ubuntu 16.10、6.2.0 上测试g++