Використання призначеного для порівняння std :: set


108

Я намагаюся змінити типовий порядок елементів у наборі цілих чисел, які мають бути лексикографічними замість числових, і я не можу отримати наступне для компіляції з 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

Ви використовуєте функцію, де як слід використовувати функтор (клас, який перевантажує оператора (), щоб його можна було назвати як функція).

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

Потім ви використовуєте назву класу як параметр типу

set<int64_t, lex_compare> s;

Якщо ви хочете уникнути кодової панелі кодової панелі функтора, ви також можете скористатися функціональним покажчиком (припустимо 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) для позначення типу функції?
Льюїс Чан

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 з ()оператором

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
ZFY

2
Це 5. божевільно. Кожен день знаходить нові куточки мови.
Ян Хошек

18

Відповідь 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!

Нічого собі, я думаю, що це вартувало клопоту!


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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.