ใช้ std :: set comparator ที่กำหนดเอง


109

ฉันพยายามเปลี่ยนลำดับเริ่มต้นของรายการในชุดจำนวนเต็มให้เป็นพจนานุกรมแทนตัวเลขและฉันไม่สามารถรวบรวมสิ่งต่อไปนี้ด้วย 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);
    ...
}

ฉันได้รับข้อผิดพลาดต่อไปนี้:

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

ผมทำอะไรผิดหรือเปล่า?

คำตอบ:


160

คุณกำลังใช้ฟังก์ชันโดยที่คุณควรใช้ functor (คลาสที่โอเวอร์โหลดตัวดำเนินการ () ดังนั้นจึงสามารถเรียกได้ว่าเป็นฟังก์ชัน)

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

จากนั้นคุณใช้ชื่อคลาสเป็นพารามิเตอร์ type

set<int64_t, lex_compare> s;

หากคุณต้องการหลีกเลี่ยงรหัสต้นแบบ functor คุณยังสามารถใช้ตัวชี้ฟังก์ชัน (สมมติว่าlex_compareเป็นฟังก์ชัน)

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

4
@Omry: ฉันสนใจที่จะรู้ว่าคุณใช้คอมไพเลอร์อะไรอยู่: codepad.org/IprafuVf

1
@Omry คุณใช้คอมไพเลอร์ตัวไหน

4
@Omry มาตรฐาน C ++ บอกว่าพารามิเตอร์เทมเพลตที่สองต้องเป็นชื่อของชนิด - ชื่อฟังก์ชันไม่ใช่ชื่อของชนิด

6
เราสามารถใช้ Decltype (lex_compare) เพื่อแสดงประเภทฟังก์ชันได้หรือไม่
Lewis Chan

2
@LewisChan คำที่ถูกต้องจะเป็นstd::set<int64_t, decltype(&lex_compare)> s(&lex_compare)
Nishant Singh

113

1. โซลูชัน C ++ 20 ที่ทันสมัย

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

เราใช้ ฟังก์ชันแลมด้าเป็นตัวเปรียบเทียบ ตามปกติตัวเปรียบเทียบควรส่งคืนค่าบูลีนโดยระบุว่าองค์ประกอบที่ส่งผ่านเนื่องจากอาร์กิวเมนต์แรกถือว่าไปก่อนหน้าวินาทีในลำดับที่เข้มงวดเฉพาะที่กำหนดไว้

การสาธิตออนไลน์

2. โซลูชัน C ++ 11 ที่ทันสมัย

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

ก่อน C ++ 20 เราต้องส่งแลมด้าเป็นอาร์กิวเมนต์เพื่อตั้งค่าตัวสร้าง

การสาธิตออนไลน์

3. คล้ายกับโซลูชันแรก แต่มีฟังก์ชันแทนแลมด้า

ทำให้ตัวเปรียบเทียบเป็นฟังก์ชันบูลีนตามปกติ

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

จากนั้นใช้วิธีนี้:

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

การสาธิตออนไลน์

หรือวิธีนี้:

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

การสาธิตออนไลน์

4. โซลูชันเก่าโดยใช้โครงสร้างร่วมกับ()ตัวดำเนินการ

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

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

การสาธิตออนไลน์

5. โซลูชันทางเลือก: สร้างโครงสร้างจากฟังก์ชันบูลีน

ใช้ฟังก์ชันบูลีน

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

และสร้างโครงสร้างจากมันโดยใช้ std::integral_constant

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

สุดท้ายใช้โครงสร้างเป็นตัวเปรียบเทียบ

std::set<X, Cmp> set;

การสาธิตออนไลน์


3
ในตัวอย่างที่ 1 จำเป็นต้องส่ง cmp ไปยังตัวสร้างหรือไม่ ชุดจะสร้างขึ้นมาเองเนื่องจากประเภทแลมบ์ดาถูกกำหนดให้เป็นประเภทเทมเพลตหรือไม่
PeteUK

2
@PeteUK ก่อนตัวเปรียบเทียบ C ++ 20 จะต้องถูกส่งผ่านไปยังตัวสร้าง ในตัวสร้าง C ++ 20 ที่ไม่มีอาร์กิวเมนต์สามารถใช้ได้ ขอบคุณสำหรับคำถาม; คำตอบได้รับการอัปเดต
diralik

1
@diralik ขอบคุณมากสำหรับการตอบรับและอัปเดตคำตอบที่ยอดเยี่ยมของคุณ
PeteUK

1
แลมด้าทั่วไปดูเหมือนจะใช้ได้กับ 1 และ 2
ด้า

2
นั่นคือ 5. บ้า เราพบสิ่งใหม่ ๆ ของภาษาทุกวัน
Jan Hošek

18

คำตอบของ Yacoby เป็นแรงบันดาลใจให้ฉันเขียนอะแดปเตอร์สำหรับห่อหุ้ม 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!

ว้าวฉันคิดว่ามันคุ้มค่ากับปัญหา!


17
เรื่องของความคิดเห็นฉันเดา

6

คุณสามารถใช้ตัวเปรียบเทียบฟังก์ชันได้โดยไม่ต้องห่อมันดังนี้:

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

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

ซึ่งเป็นเรื่องที่น่ารำคาญในการพิมพ์ทุกครั้งที่คุณต้องการชุดประเภทนั้นและอาจทำให้เกิดปัญหาได้หากคุณไม่สร้างชุดทั้งหมดด้วยตัวเปรียบเทียบเดียวกัน


3

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;

ซึ่งอนุญาตให้คุณทำ:

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

รวบรวมและเรียกใช้:

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

สามารถดูข้อมูลเพิ่มเติมstd::less<>ได้ที่: ตัวเปรียบเทียบแบบโปร่งใสคืออะไร?

ทดสอบบน Ubuntu 16.10, g++6.2.0

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.