2013-05-02 8 views
0

Ну, моя проблема заключается в том, что я использую зЬй :: установить с помощью пользовательского компаратора, что-то вроде:C++ станд :: набора пользовательского компаратор

class A 
{ 
public: 
    A(int x, int y): 
     _x(x), _y(y) 
    { 
    } 

    int hashCode(){ return (_y << 16) | _x; } 

private: 
    short int _y; 
    short int _x; 
}; 

struct comp 
{ 
    bool operator() (A* g1, A* g2) const 
    { 
     return g1->hashCode() < g2->hashCode(); 
    } 
}; 

Итак, я использую его как

std::set<A*, comp> myset; 

// Insert some data 
A* a = new A(2,1); 
A* b = new A(1,3); 
myset.insert(a); 
myset.insert(b); 

Теперь моя проблема заключается в том, что я хотел бы сделать это:

myset.find((2 << 16) | 1); 

Но, конечно, это excepts A * не короткий Int.

Итак, я знаю, что могу использовать std :: find_if, но разве это не сделает бесполезным пользовательский компаратор? Он будет перебирать весь список, не так ли? Есть ли способ использовать find с hashCode, а не сам объект?

Спасибо!

+0

Как насчет std :: find_if с компаратором (с поправкой на равенство)? – stardust

+0

Извините, что я имел в виду, когда писал 'std :: find', это должно быть' std :: find_if'. Разве он не повторил бы весь список и не оптимизировал бы поиск вообще? Моя причина использования 'std :: set' - это стоимость поиска O (log (n)). – Guillem

ответ

3

set::find принимает аргумент типа key_type (см. Обсуждение Why is set::find not a template?). Используя std :: set, вы должны создать временный объект для использования find.

myset.find(A(2, 1)); 

Если А не дешево построить вы можете использовать std::map<int, A> (или обертку вокруг него) вместо этого.

+0

A действительно не дешево. Если бы я использовал карту, было бы лучше использовать 'map' или' unordered_map'. В настоящее время я использую 'google :: dense_hash_map' для некоторых других карт с ключами int. Вставить стоимость не важно вообще, поиск стоит. – Guillem

+0

@ProStage Если заказ не имеет значения, вы должны использовать 'unordered_map' или' unordered_set'. – hansmaad

+0

@ProStage «A» вы показываете невероятно дешево. –

1

std::setstd::set<>::find не имеет значения (элемент); аргумент должен быть типа ключа. Для простых классов, подобных вашим, вполне вероятно, что использование std::vector<A> и сохранение его сортировки (используя std::lower_bound для поиска и в качестве точки вставки) будет таким же быстрым. И с std::lower_bound, вы можете передать в качестве сравнения, и использовать любой тип, который вы хотите как ключ. Все, что вам нужно сделать, это убедиться, что ваш comp класс может обрабатывать смешанные сравнения типа, например:

struct Comp 
{ 
    bool operator()(A const&, B const&) const; 
    bool operator()(A const&, int) const; 
    bool operator()(int, A const&) const; 
}; 
+0

Дело в том, что класс не такой простой. Он фактически содержит одну карту, которая может вырасти до 1000 элементов. Количество объектов 'A' также может вырасти до 1000+, причем все они уникальны. Время поиска/стоимость важны, и вектор не будет вариантом. Кроме того, при вставке/стирании возникают проблемы с потоками, поскольку все итераторы становятся недействительными. – Guillem

+0

@ProStage Затем вам нужно будет найти работу. Вектор 'std :: shared_ptr' может сделать трюк; альтернативно, вы можете попытаться реализовать «фиктивную» версию 'A', которая не стоит строить, и которая может быть использована как ключ в' std :: set'. («Карта» без записей не дорогая для сборки. Или просто переместите реализацию или, по крайней мере, ее дорогие части, в делегат, тогда экземпляр с нулевым указателем на делегат может быть создан как индекс и если делегату управляют с помощью умного указателя, копия может оказаться очень дешевой.) –

0
myset.find(&A(2, 1)); 

Или

A a(2, 1); 
myset.find(&a); 
+1

Первый не является законным C++. –

+0

Зависит от компилятора, но да, это плохой стиль ... –

+0

Это не зависит от компилятора. Стандарт явно запрещает его (если только класс 'A' не переопределил' operator &');, то компилятор принимает его (когда он вызывается как компилятор C++), он сломан. (Я действительно был бы удивлен, если компилятор его принял. , в конце концов, датируется с самых ранних дней C. Но я был удивлен раньше того, что принимают некоторые компиляторы.) –

0

Вы определили std::set<A*, comp> myset;, поэтому std::find() должен принять A* аргумент.

std::set<A*, comp> myset; 

// Insert some data 
A* a = new A(2,1); 
A* b = new A(1,3); 
myset.insert(a); 
myset.insert(b); 

Затем вам нужно сделать

myset.find(&A(2,1)) 

Назад к вашему вопросу, std::find() не принимает пользовательский компаратор. Вам нужно, по сути, использовать std::find_if.

Смежные вопросы