Özel std :: set karşılaştırıcı kullanma


110

Bir tamsayı kümesindeki öğelerin varsayılan sırasını sayısal yerine sözlükbilimsel olacak şekilde değiştirmeye çalışıyorum ve aşağıdakileri g ++ ile derleyemiyorum:

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

Şu hatayı alıyorum:

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

Neyi yanlış yapıyorum?

Yanıtlar:


160

Bir functor kullanmanız gereken yerde bir fonksiyon kullanıyorsunuz (() operatörünü aşırı yükleyen bir sınıf, böylece bir fonksiyon gibi çağrılabilir).

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

Daha sonra sınıf adını tür parametresi olarak kullanırsınız

set<int64_t, lex_compare> s;

İşlevsel ortak koddan kaçınmak istiyorsanız, bir işlev işaretçisi de kullanabilirsiniz (bir işlev olduğunu varsayarak lex_compare).

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

4
@Omry: Ben bilerek ilgilenen olurdum kullandığınız derlemek: codepad.org/IprafuVf

1
@Omry Hangi derleyiciyi kullanıyorsunuz?

4
@Omry C ++ standardı, ikinci şablon parametresinin bir türün adı olması gerektiğini söyler - bir işlev adı, bir türün adı değildir.

6
işlev türünü belirtmek için decltype (lex_compare) kullanabilir miyiz?
Lewis Chan

2
@LewisChan doğru terimstd::set<int64_t, decltype(&lex_compare)> s(&lex_compare)
Nishant Singh

114

1. Modern C ++ 20 çözümü

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

Kullanırız Karşılaştırıcı olarak lambda fonksiyonunu . Her zaman olduğu gibi, karşılaştırıcı, ilk bağımsız değişken olarak iletilen öğenin tanımladığı belirli katı zayıf sıralamada ikinciden önce gelip gelmediğini gösteren boole değerini döndürmelidir .

Çevrimiçi demo

2. Modern C ++ 11 çözümü

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

C ++ 20'den önce, yapıcıyı ayarlamak için argüman olarak lambda geçirmemiz gerekir

Çevrimiçi demo

3. İlk çözüme benzer, ancak lambda yerine işlevle

Karşılaştırıcıyı normal boole işlevi olarak yapın

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

O zaman şu şekilde kullanın:

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

Çevrimiçi demo

veya bu şekilde:

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

Çevrimiçi demo

4. ()Operatörlü yapı kullanan eski çözüm

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

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

Çevrimiçi demo

5. Alternatif çözüm: boole işlevinden yapı oluşturun

Boole işlevini al

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

Ve kullanarak ondan yapı oluşturun std::integral_constant

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

Son olarak, yapıyı karşılaştırıcı olarak kullanın

std::set<X, Cmp> set;

Çevrimiçi demo


3
Örnek 1'de cmp'nin yapıcıya iletilmesi gerekiyor mu? Lambda türü şablon türü olarak verildiği için küme kendisini bir tane oluşturacak mı?
PeteUK

2
@PeteUK önce C ++ 20 karşılaştırıcısının yapıcıya geçirilmesi gerekir. C ++ 20'de argümansız yapıcı kullanılabilir. Sorunuz için teşekkür ederim; cevap güncellendi
diralik

1
@diralik Cevabınız ve zaten harika cevabınızı güncellemeniz için çok teşekkür ederim.
PeteUK

2
Bu 5. delilik. Her gün dilin yeni köşeleri ve çatlakları bulunur.
Jan Hošek

18

Yacoby'nin cevabı, functor kazan plakasını kapsüllemek için bir adaptör yazmam için bana ilham veriyor.

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!

Vay be, bence bu zahmete değdi!


17
Bir fikir meselesi sanırım.

6

Bir işlev karşılaştırıcısını şu şekilde sarmadan kullanabilirsiniz:

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

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

Bu türden bir sete her ihtiyaç duyduğunuzda yazmak rahatsız edicidir ve tüm setleri aynı karşılaştırıcıyla oluşturmazsanız sorunlara neden olabilir.


3

std::less<> özel sınıfları kullanırken operator<

Eğer operator<tanımlanmış özel sınıfınız ile uğraşıyorsanız , sadece kullanabilirsiniz std::less<>.

Http://en.cppreference.com/w/cpp/container/set/find C ++ 14 adresinde belirtildiği gibi iki yeni findAPI ekledi :

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

yapmanıza izin veren:

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

Derleyin ve çalıştırın:

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

Daha fazla bilgiyi std::less<>şu adreste bulabilirsiniz: Şeffaf karşılaştırıcılar nedir?

Ubuntu 16.10, g++6.2.0'da test edilmiştir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.