Unordered_map с пользовательским классом в качестве ключа [дубликат]

Отличные ответы выше. Я хочу только добавить, что внутри области вашей функции вы можете присвоить значение этой переменной переменной let self = this;, а затем внутри обратного вызова просто обратиться к данным типа self.data.

Ваш код :

function MyConstructor(data, transport) {
    this.data = data;

    let self = this;   //ADD THIS LINE

    transport.on('data', function () {
        alert(self.data);   //USE IT LIKE THIS
    });
}

// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};

// called as
var obj = new MyConstructor('foo', transport);
201
задан Adam Stelmaszczyk 7 December 2015 в 23:51
поделиться

1 ответ

Чтобы иметь возможность использовать std::unordered_map (или один из других неупорядоченных ассоциативных контейнеров) с пользовательским ключом, вам необходимо определить две вещи:

  1. Хеш-функция ; это должен быть класс, который переопределяет operator() и вычисляет хэш-значение, заданное объектом типа ключа. Один особенно прямой способ сделать это - специализировать шаблон std::hash для вашего ключевого типа.
  2. Функция сравнения для равенства; это необходимо, потому что хеш не может полагаться на то, что хеш-функция всегда будет предоставлять уникальное значение хэша для каждого отдельного ключа (т. е. он должен иметь возможность иметь дело с коллизиями), поэтому ему нужен способ сравнения двух заданных ключей для точного соответствия. Вы можете реализовать это либо как класс, который переопределяет operator(), либо как специализацию std::equal, или & ndash; самый легкий из всех & ndash; перегружая operator==() для вашего типа ключа (как вы уже сделали).

Трудность с хеш-функцией заключается в том, что если ваш тип ключа состоит из нескольких членов, вы обычно будете иметь хэш функция вычисляет значения хэша для отдельных членов, а затем каким-то образом объединяет их в одно значение хэша для всего объекта. Для хорошей производительности (т. Е. Нескольких столкновений) вы должны тщательно подумать о том, как объединить отдельные хэш-значения, чтобы вы не слишком часто получали одинаковый вывод для разных объектов.

Хорошая отправная точка для хэша функция - это функция, которая использует смещение битов и побитовое XOR для объединения отдельных хеш-значений. Например, предполагая такой тип ключа:

struct Key
{
  std::string first;
  std::string second;
  int         third;

  bool operator==(const Key &other) const
  { return (first == other.first
            && second == other.second
            && third == other.third);
  }
};

Вот простая хеш-функция (адаптированная из той, которая используется в примере cppreference для пользовательских хеш-функций ):

namespace std {

  template <>
  struct hash<Key>
  {
    std::size_t operator()(const Key& k) const
    {
      using std::size_t;
      using std::hash;
      using std::string;

      // Compute individual hash values for first,
      // second and third and combine them using XOR
      // and bit shifting:

      return ((hash<string>()(k.first)
               ^ (hash<string>()(k.second) << 1)) >> 1)
               ^ (hash<int>()(k.third) << 1);
    }
  };

}

При этом вы можете создать экземпляр std::unordered_map для ключевого типа:

int main()
{
  std::unordered_map<Key,std::string> m6 = {
    { {"John", "Doe", 12}, "example"},
    { {"Mary", "Sue", 21}, "another"}
  };
}

Он будет автоматически использовать std::hash<Key>, как определено выше для вычислений значений хэш-функции и operator==, определенных как функция-член Key для проверок равенства.

Если вы не хотите специализировать шаблон внутри пространства имен std (хотя это совершенно законно в этом случае), вы можете определить хеш-функцию как отдельный класс и добавить ее в список аргументов шаблона для карты:

struct KeyHasher
{
  std::size_t operator()(const Key& k) const
  {
    using std::size_t;
    using std::hash;
    using std::string;

    return ((hash<string>()(k.first)
             ^ (hash<string>()(k.second) << 1)) >> 1)
             ^ (hash<int>()(k.third) << 1);
  }
};

int main()
{
  std::unordered_map<Key,std::string,KeyHasher> m6 = {
    { {"John", "Doe", 12}, "example"},
    { {"Mary", "Sue", 21}, "another"}
  };
}

Как определить лучшую хеш-функцию? Как было сказано выше, определение хорошей хэш-функции важно, чтобы избежать столкновений и получить хорошую производительность. Для действительно хорошего вам необходимо учитывать распределение возможных значений всех полей и определять хеш-функцию, которая прогнозирует распределение в пространстве возможных результатов, как можно более широкое и равномерно распределенное.

может быть трудным; метод XOR / бит-сдвига выше, вероятно, не плохой старт. Для немного лучшего начала вы можете использовать шаблон функции hash_value и hash_combine в библиотеке Boost. Первый действует аналогично std::hash для стандартных типов (в последнее время также включает кортежи и другие полезные стандартные типы); последний помогает вам объединить отдельные хэш-значения в один. Ниже приведена перепись хэш-функции, использующей вспомогательные функции Boost:

#include <boost/functional/hash.hpp>

struct KeyHasher
{
  std::size_t operator()(const Key& k) const
  {
      using boost::hash_value;
      using boost::hash_combine;

      // Start with a hash value of 0    .
      std::size_t seed = 0;

      // Modify 'seed' by XORing and bit-shifting in
      // one member of 'Key' after the other:
      hash_combine(seed,hash_value(k.first));
      hash_combine(seed,hash_value(k.second));
      hash_combine(seed,hash_value(k.third));

      // Return the result.
      return seed;
  }
};

И вот переписывание, которое не использует boost, но использует хороший метод объединения хэшей:

namespace std
{
    template <>
    struct hash<Key>
    {
        size_t operator()( const Key& k ) const
        {
            // Compute individual hash values for first, second and third
            // http://stackoverflow.com/a/1646913/126995
            size_t res = 17;
            res = res * 31 + hash<string>()( k.first );
            res = res * 31 + hash<string>()( k.second );
            res = res * 31 + hash<int>()( k.third );
            return res;
        }
    };
}
344
ответ дан Soonts 21 August 2018 в 01:33
поделиться
  • 1
    Не могли бы вы объяснить, почему необходимо сдвинуть бит в KeyHasher? – Chani 12 August 2014 в 18:39
  • 2
    Если вы не сдвинули биты, а две строки были одинаковыми, xor заставил бы их отменить друг друга. Таким образом, хэш («a», «a», 1) будет таким же, как хэш («b», «b», 1). Также порядок не имеет значения, поэтому хэш («a», «b», 1) будет таким же, как хэш («b», «a», 1). – Buge 29 August 2014 в 16:40
  • 3
    Я просто изучаю C ++, и с одной вещью, с которой я всегда сталкиваюсь: где поставить код? Я написал специальный метод std::hash для моего ключа, как вы это сделали. Я поставил это в нижней части моего файла Key.cpp, но я получаю следующую ошибку: Error 57 error C2440: 'type cast' : cannot convert from 'const Key' to 'size_t' c:\program files (x86)\microsoft visual studio 10.0\vc\include\xfunctional. Я предполагаю, что компилятор не находит мой хэш-метод? Должен ли я добавлять что-либо в файл Key.h? – Ben 7 February 2015 в 18:13
  • 4
    @Ben Вставка файла .h верна. std::hash на самом деле не является структурой, а шаблоном (специализацией) для структуры . Таким образом, это не реализация - она ​​будет преобразована в реализацию, когда компилятор нуждается в ней. Шаблоны всегда должны входить в заголовочные файлы. См. Также stackoverflow.com/questions/495021/… – jogojapan 8 February 2015 в 02:02
  • 5
    @nightfury find() возвращает итератор, и этот итератор указывает на «запись». карты. Запись - это std::pair, состоящая из ключа и значения. Поэтому, если вы сделаете auto iter = m6.find({"John","Doe",12});, вы получите ключ в iter->first и значение (т. Е. Строку "example") в iter->second. Если вы хотите, чтобы строка была напрямую, вы можете использовать m6.at({"John","Doe",12}) (который выдает исключение, если ключ не выходит) или m6[{"John","Doe",12}] (который создаст пустое значение, если ключ не существует). – jogojapan 21 April 2015 в 08:12
Другие вопросы по тегам:

Похожие вопросы: