Где я должен поместить специализированный std::hash для пользовательского типа

Я искал много страниц, и я думаю, что знал, как написать std::hash. Но я не знаю, где это поставить.

Пример представлен здесь http://en.cppreference.com/w/cpp/utility/hash.

Тем не менее, я определил свой тип Instance в пространстве имен ca в файле instance_management.h, Я хочу использовать unordered_set<Instance> в том же файле в другом классе InstanceManager, Поэтому я пишу следующий код:

namespace std
{
    template <> struct hash<ca::Instance>
    {
        size_t operator()(const ca::Instance & instance) const
        {
            std::size_t seed = 0;
            // Some hash value calculation here.
            return seed;
        }
    };
} // namespace std

Но куда мне его положить? Я пробовал много мест, но все не удалось.

Я использую Visual Studio 2013. Я пытался поместить предыдущий код в некоторых местах, но все не смогли его скомпилировать.

// location 1

namespace ca
{
    class Instance {...}
    class InstanceManager
    {
        // ... some other things.
        private unordered_set<Instance>;
    }
}

// location 2

3 ответа

Решение

Спасибо всем.

Я нашел причину и решил проблему как-то: визуальная студия приняла InstanceHash когда я определял instances_, Так как я менял использование set в unordered_setЯ забыл уточнить InstanceHash когда я пытался получить const_iteratorпоэтому на этот раз компилятор попытался использовать std::hash<> вещи и не удалось. Но компилятор не нашел строку, используя const_iteratorпоэтому я ошибочно подумал, что не принял InstanceHash когда я определял instances_,

Я также пытался специализировать std::hash<> для экземпляра класса. Однако эта специализация требует как минимум объявления класса ca::Instance и некоторые из его функций-членов для вычисления значения хеш-функции. После этой специализации, определение класса ca::InstanceManage буду использовать это.

Сейчас я обычно помещаю объявления и реализации почти всех классов и функций-членов вместе. Итак, что мне нужно сделать, это, вероятно, разделить ca объем пространства имен на 2 части и положить std{ template <> struct hash<ca::Instance>{...}} в середине.

Есть несколько способов.

специализация std::hash

В своем коде убедитесь, что ваш std::hash<Instance> специализации предшествует сразу Instance определение класса, а затем с использованием unordered_set контейнер, который использует его.

namespace ca
{
    class Instance {...};

}

namespaces std {

    template<> hash<Instance> { ... };

}

namespace ca {

    class InstanceManager
    {
        // ... some other things.
        private unordered_set<Instance>;
    }
}

Одним из недостатков является то, что при передаче std::hash<ca::Instance> к другим функциям. Причина в том, что связанное пространство имен (ca) из всех аргументов шаблона std::hash может использоваться во время поиска имени (ADL). Такие ошибки немного редки, но если они возникают, их трудно отладить.

Смотрите этот вопрос и ответы для более подробной информации.

Передавая свой хэш unordered_set

struct MyInstanceHash { ... };

using MyUnorderedSet = std:unordered_set<Instance, MyInstanceHash>;

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

С помощью hash_append

Обратите внимание, однако, что предложение по Стандарту N3980 в настоящее время ожидает рассмотрения. Это предложение отличается гораздо более совершенным дизайном, в котором используется универсальная хеш-функция, которая принимает произвольный поток байтов для хеширования его параметром шаблона (фактическим алгоритмом хеширования).

template <class HashAlgorithm>
struct uhash
{
    using result_type = typename HashAlgorithm::result_type;

    template <class T>
    result_type
    operator()(T const& t) const noexcept
    {
        HashAlgorithm h;
        using std::hash_append;
        hash_append(h, t);
        return static_cast<result_type>(h);
    }
};

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

class X
{
    std::tuple<short, unsigned char, unsigned char> date_;
    std::vector<std::pair<int, int>>                data_;

public:
    // ...
    friend bool operator==(X const& x, X const& y)
    {
        return std::tie(x.date_, x.data_) == std::tie(y.date_, y.data_);
    }

    // Hook into the system like this
    template <class HashAlgorithm>
    friend void hash_append(HashAlgorithm& h, X const& x) noexcept
    {
        using std::hash_append;
        hash_append(h, x.date_);
        hash_append(h, x.data_);
    }
}

Для получения дополнительной информации см. Презентацию автора @HowardHinnant на CppCon14 ( слайды, видео). Полный исходный код автора и Bloomberg доступен.

Не специализируется std::hashвместо этого напишите свой собственный объект хеш-функции (см. Edge_Hash ниже) и объявите unordered_set с двумя аргументами шаблона.

#include <unordered_set>
#include <functional>

namespace foo
{
    // an edge is a link between two nodes
    struct Edge
    {
        size_t src, dst;
    };

    // this is an example of symmetric hash (suitable for undirected graphs)
    struct Edge_Hash
    {
        inline size_t operator() ( const Edge& e ) const
        {
            static std::hash<size_t> H;
            return H(e.src) ^ H(e.dst);
        }
    };

    // this keeps all edges in a set based on their hash value
    struct Edge_Set
    {
        // I think this is what you're trying to do?
        std::unordered_set<Edge,Edge_Hash> edges;
    };
}

int main()
{
    foo::Edge_Set e;
}

Связанные посты, например:

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