Sử dụng trình so sánh std :: set tùy chỉnh


106

Tôi đang cố gắng thay đổi thứ tự mặc định của các mục trong một tập hợp các số nguyên thành từ vựng thay vì số và tôi không thể lấy phần sau để biên dịch với 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);
    ...
}

Tôi nhận được lỗi sau đây:

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

tôi đang làm gì sai

Câu trả lời:


159

Bạn đang sử dụng một hàm mà khi bạn nên sử dụng hàm functor (một lớp nạp chồng toán tử () để nó có thể được gọi như một hàm).

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

Sau đó, bạn sử dụng tên lớp làm tham số kiểu

set<int64_t, lex_compare> s;

Nếu bạn muốn tránh mã viết sẵn của functor, bạn cũng có thể sử dụng một con trỏ hàm (giả sử lex_comparelà một hàm).

set<int64_t, bool(*)(const int64_t& lhs, const int64_t& rhs)> s(&lex_compare);

4
@Omry: Tôi muốn biết bạn đang sử dụng trình biên dịch nào: codepad.org/IprafuVf

1
@Omry Bạn đang sử dụng trình biên dịch nào?

4
@Omry Tiêu chuẩn C ++ nói rằng tham số mẫu thứ hai phải là tên của một kiểu - tên hàm không phải là tên của một kiểu.

6
chúng ta có thể sử dụng kiểu khai báo (lex_compare) để biểu thị kiểu hàm không?
Lewis Chan

2
@LewisChan thuật ngữ chính xác sẽ làstd::set<int64_t, decltype(&lex_compare)> s(&lex_compare)
Nishant Singh

109

1. Giải pháp C ++ 20 hiện đại

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

Chúng tôi sử dụng hàm lambda làm bộ so sánh. Như thường lệ, bộ so sánh sẽ trả về giá trị boolean, cho biết liệu phần tử được truyền làm đối số đầu tiên có được coi là đi trước phần tử thứ hai theo thứ tự yếu nghiêm ngặt cụ thể mà nó xác định hay không.

Bản demo trực tuyến

2. Giải pháp C ++ 11 hiện đại

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

Trước C ++ 20, chúng ta cần truyền lambda làm đối số để đặt hàm tạo

Bản demo trực tuyến

3. Tương tự như giải pháp đầu tiên, nhưng với hàm thay vì lambda

Tạo bộ so sánh như hàm boolean thông thường

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

Sau đó, sử dụng nó, theo cách này:

std::set<int, decltype(cmp)*> s(cmp);

Bản demo trực tuyến

hoặc theo cách này:

std::set<int, decltype(&cmp)> s(&cmp);

Bản demo trực tuyến

4. Giải pháp cũ sử dụng struct với ()toán tử

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

// ...
// later
std::set<int, cmp> s;

Bản demo trực tuyến

5. Giải pháp thay thế: tạo struct từ hàm boolean

Lấy hàm boolean

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

Và tạo cấu trúc từ nó bằng cách sử dụng std::integral_constant

#include <type_traits>
using Cmp = std::integral_constant<decltype(&cmp), &cmp>;

Cuối cùng, sử dụng struct làm trình so sánh

std::set<X, Cmp> set;

Bản demo trực tuyến


3
Trong ví dụ 1, cmp có cần phải được truyền vào hàm tạo không? Tập hợp có tự xây dựng một tập hợp như kiểu lambda được đưa ra dưới dạng kiểu mẫu không?
PeteUK

2
@PeteUK trước bộ so sánh C ++ 20 phải được chuyển vào hàm tạo. Trong C ++ 20 hàm tạo không có đối số có thể được sử dụng. Cảm ơn bạn đã đặt câu hỏi; câu trả lời đã được cập nhật
diralik

1
@diralik Cảm ơn bạn rất nhiều vì đã phản hồi và cập nhật câu trả lời đã rất tuyệt vời của bạn.
PeteUK

1
lambda chung có vẻ cũng hoạt động cho 1 và 2
ZFY

2
Điều đó 5. là mất trí. Người ta tìm thấy những ngóc ngách mới của ngôn ngữ mỗi ngày.
Jan Hošek

18

Câu trả lời của Yacoby đã truyền cảm hứng cho tôi viết một bộ điều hợp để đóng gói tấm biểu đồ bộ functor.

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!

Chà, tôi nghĩ điều đó thật đáng giá!


17
Tôi đoán là một vấn đề quan điểm.

6

Bạn có thể sử dụng bộ so sánh hàm mà không cần gói nó như vậy:

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

std::set<MyType, bool(*)(const MyType&, const MyType&)> mySet(&comparator);

điều này khiến bạn khó chịu khi phải gõ ra mỗi khi bạn cần một bộ thuộc loại đó và có thể gây ra sự cố nếu bạn không tạo tất cả các bộ với cùng một bộ so sánh.


3

std::less<> khi sử dụng các lớp tùy chỉnh với operator<

Nếu bạn đang xử lý một tập hợp lớp tùy chỉnh của mình đã operator<được xác định, thì bạn có thể sử dụng std::less<>.

Như đã đề cập tại http://en.cppreference.com/w/cpp/container/set/find C ++ 14 đã thêm hai findAPI mới :

template< class K > iterator find( const K& x );
template< class K > const_iterator find( const K& x ) const;

cho phép bạn làm:

main.cpp

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

Biên dịch và chạy:

g++ -std=c++14 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

std::less<>thể tìm thấy thêm thông tin về : Bộ so sánh minh bạch là gì?

Đã thử nghiệm trên Ubuntu 16.10, g++6.2.0.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.