Вставка в unordered_set с пользовательской хеш-функцией

У меня есть следующий код, чтобы сделать unordered_set<Interval>, Это хорошо компилируется.

struct Interval {
  unsigned int begin;
  unsigned int end;
  bool updated;   //true if concat.  initially false
  int patternIndex;  //pattern index. valid for single pattern
  int proteinIndex;   //protein index.  for retrieving the pattern
};

struct Hash {
  size_t operator()(const Interval &interval);
};


size_t Hash::operator()(const Interval &interval){
  string temp = to_string(interval.begin) + to_string(interval.end) + to_string(interval.proteinIndex);
  return hash<string>()(temp);
}

unordered_set<Interval, string, Hash> test;

Тем не менее, я не могу скомпилировать, когда я пытаюсь вставить, используя этот код:

  for(list<Interval>::iterator i = concat.begin(); i != concat.end(); ++i){
    test.insert((*i));
  }

Более того, я не могу определить, в чем проблема из сообщений об ошибках.

Вот образец:

note: candidate is:
note: size_t Hash::operator()(const Interval&)
note:   candidate expects 1 argument, 2 provided  

Я думал, что предоставил только 1 аргумент...

Кто-нибудь видит проблему с моим кодом вставки? Пожалуйста, помогите, если сможете - я давно ищу решение.

РЕДАКТИРОВАТЬ:

Вот новый экземпляр кода: unordered_set<Interval, Hash> test;Тем не менее, я все еще получаю множество сообщений об ошибках. Пример:

note: candidate is:
note: size_t Hash::operator()(const Interval&) <near match>
note:   no known conversion for implicit ‘this’ parameter from ‘const Hash*’ to ‘Hash*’

1 ответ

Решение

Первая проблема:

Вы проходите string в качестве второго аргумента шаблона для вашего экземпляра unordered_set<> шаблон класса. Второй аргумент должен быть типом вашего хеш-функтора, и std::string не вызываемый объект.

Возможно, хотел написать:

unordered_set<Interval, /* string */ Hash> test;
//                      ^^^^^^^^^^^^
//                      Why this?

Кроме того, я бы предложил использовать имена, отличные от begin а также end для ваших (членов) переменных, так как это имена алгоритмов стандартной библиотеки C++.

Вторая проблема:

Следует помнить, что хэш-функция должна быть квалифицирована какconstпоэтому ваш функтор должен быть:

struct Hash {
   size_t operator() (const Interval &interval) const {
   //                                           ^^^^^
   //                                           Don't forget this!
     string temp = to_string(interval.b) + 
                   to_string(interval.e) + 
                   to_string(interval.proteinIndex);
     return (temp.length());
   }
};

Третья проблема:

Наконец, если вы хотите std::unordered_set уметь работать с объектами типа Intervalвам нужно определить оператор равенства в соответствии с вашей хэш-функцией. По умолчанию, если вы не указали аргумент типа в качестве третьего параметра std::unordered_set шаблон класса, operator == будет использоваться.

В настоящее время у вас нет перегрузки operator == для вашего класса Interval, так что вы должны предоставить один. Например:

inline bool operator == (Interval const& lhs, Interval const& rhs)
{
    return (lhs.b == rhs.b) && 
           (lhs.e == rhs.e) && 
           (lhs.proteinIndex == rhs.proteinIndex); 
}

Заключение:

После всех вышеперечисленных изменений вы можете увидеть, как ваш код компилируется в этом живом примере.

Я думаю, Энди Проул отлично исправил проблемы с вашим кодом. Тем не менее, я бы добавил следующую функцию-член к вашему Interval, который описывает, что делает два интервала идентичными:

std::string getID() const { return std::to_string(b) + " " + std::to_string(e) + " " + std::to_string(proteinIndex); }

Пожалуйста, обратите внимание, что я также последовал предложению Энди Prowl и переименовал членов begin в b а также end в e, Далее вы можете легко определить хеш-функции и функции сравнения, используя лямбда-выражения. В результате вы можете определить свой unordered_set следующее:

auto hash = [](const Interval& i){ return std::hash<std::string>()(i.getID()); };
auto equal = [](const Interval& i1, const Interval& i2){ return i1.getID() == i2.getID(); };
std::unordered_set<Interval, decltype(hash), decltype(equal)> test(8, hash, equal);

Наконец, для удобства чтения я преобразовал for цикл в диапазоне на основе for цикл:

std::list<Interval> concat {{1, 2, false, 3, 4}, {2, 3, false, 4, 5}, {1, 2, true, 7, 4}};

for (auto const &i : concat)
    test.insert(i);

for (auto const &i : test)
    std::cout << i.b << ", " << i.e << ", " << i.updated << std::endl;

Вывод (я только что напечатал первые три члена каждого Interval):

2, 3, 0
1, 2, 0

Как видите, напечатаны только два интервала. Третий ({1, 2, true, 7, 4}) не был вставлен в concat, потому что это b, e, а также proteinIndexравны первому интервалу ({1, 2, false, 3, 4}).

Код на Ideone

Другие вопросы по тегам