У меня есть собственный идентификатор класса, показанный ниже, и мне нужна помощь в понимании ошибки времени компиляции, которую я вижу из-за определения хеш-функции для пользовательского идентификатора класса пользователя. Класс выглядит, как показано ниже
struct Identifier {
Identifier(const std::string &domain,
const std::string &name,
const std::string &version)
: domain(domain), name(name), version(version) {}
/// The domain associated with the identifier.
std::string domain;
/// The name associated with the identifier.
std::string name;
/// The version associated with the identifier.
std::string version;
};
inline bool operator==(const Identifier& lhs, const Identifier& rhs){
return ((lhs.domain == rhs.domain) && (lhs.name == rhs.name) && (lhs.version == rhs.version));
}
У меня есть интерфейс ModuleInterface.h, который зависит от идентификатора и выглядит следующим образом:
class ModuleInterface {
public:
using Identifiers = std::unordered_set<Identifier>;
virtual Identifiers getSupportedIdentifiers() = 0;
};
Существует класс Manager, который имеет метод RegisterModule, который принимает ModuleInterfaces, показанный ниже.
class Manager{
public:
bool registerModule(const std::shared_ptr<ModuleInterface> &module);
private:
std::map<Identifier, std::shared_ptr<ModuleInterface>> m_requestHandlers;
};
Реализация класса Manager.cpp выглядит следующим образом.
bool Manager::registerModule(const std::shared_ptr<ModuleInterface>& module){
/*
for(auto& requestIdentifier : module->getSupportedIdentifiers()){
//m_requestHandlers[requestIdentifier] = module;
} */
for (auto itr = module->getSupportedIdentifiers().begin(); itr != module->getSupportedIdentifiers().end(); ++itr) {
m_requestHandlers[*itr] = module;
}
return true;
}
Теперь, когда я пытаюсь скомпилировать его, я вижу ошибку времени компиляции, и ошибка выглядит следующим образом:
foo/src/Manager.cpp:17:21: warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl]
for (auto itr = module->getSupportedIdentifiers().begin(); itr != module->getSupportedIdentifiers().end(); ++itr) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from foo/src/Manager.cpp:2:
In file included from foo/include/Registration/Manager.h:10:
In file included from bar/usr/include/c++/v1/map:550:
In file included from bar/usr/include/c++/v1/functional:515:
In file included from bar/usr/include/c++/v1/__functional/boyer_moore_searcher.h:25:
In file included from bar/usr/include/c++/v1/unordered_map:523:
bar/usr/include/c++/v1/__hash_table:838:5: error: static assertion failed due to requirement 'integral_constant<bool, false>::value': the specified hash does not meet the Hash requirements
static_assert(__check_hash_requirements<_Key, _Hash>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bar/usr/include/c++/v1/__hash_table:853:1: note: in instantiation of template class 'std::__enforce_unordered_container_requirements<Identifier, std::hash<Identifier>, std::equal_to<Identifier>>' requested here
typename __enforce_unordered_container_requirements<_Key, _Hash, _Equal>::type
^
bar/usr/include/c++/v1/unordered_set:614:30: note: while substituting explicitly-specified template arguments into function template '__diagnose_unordered_container_requirements'
static_assert(sizeof(__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");
^
foo/src/Manager.cpp:17:29: note: in instantiation of member function 'std::unordered_set<Identifier>::~unordered_set' requested here
for (auto itr = module->getSupportedIdentifiers().begin(); itr != module->getSupportedIdentifiers().end(); ++itr) {
^
1 warning and 1 error generated.
ninja: build stopped: subcommand f
Я буду очень признателен за любую помощь или указатели, которые помогут понять, чего мне не хватает, поскольку я не понимаю, почему хеш-метод не соответствует требованию, поскольку он возвращает логическое значение на основе левого и правого углов.
---обновить публикацию минимального примера воспроизведения ---
Идентификатор.h
#pragma once
#include <string>
struct Identifier {
Identifier(const std::string &domain,
const std::string &name,
const std::string &version)
: domain(domain), name(name), version(version) {}
/// The domain associated with the identifier.
std::string domain;
/// The name associated with the identifier.
std::string name;
/// The version associated with the identifier.
std::string version;
};
inline bool operator==(const Identifier& lhs, const Identifier& rhs){
return ((lhs.domain == rhs.domain) && (lhs.name == rhs.name) && (lhs.version == rhs.version));
}
МодульИнтерфейс.h
#include "Identifier.h"
#include <unordered_set>
class ModuleInterface {
public:
using Identifiers = std::unordered_set<Identifier>;
virtual Identifiers getSupportedIdentifiers() = 0;
};
Менеджер.h
#pragma once
#include "Identifier.h"
#include "ModuleInterface.h"
#include <map>
namespace test {
class Manager {
public:
Manager() = default;
bool registerModule(const std::shared_ptr<ModuleInterface>& module);
private:
std::map<Identifier, std::shared_ptr<ModuleInterface>> m_requestHandlers;
};
}
Менеджер.cpp
#include "Manager.h"
namespace test{
bool Manager::registerModule(const std::shared_ptr<ModuleInterface>& module){
for(auto& requestIdentifier : module->getSupportedIdentifiers()){
m_requestHandlers[requestIdentifier] = module;
}
/*
for (auto itr = module->getSupportedIdentifiers().begin(); itr != module->getSupportedIdentifiers().end(); ++itr) {
m_requestHandlers[*itr] = module;
}*/
return true;
}
}
Ошибка
/Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk/usr/include/c++/v1/__hash_table:697:5: error: static assertion failed due to requirement 'integral_constant<bool, false>::value': the specified hash does not meet the Hash requirements
static_assert(__check_hash_requirements<_Key, _Hash>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk/usr/include/c++/v1/__hash_table:712:1: note: in instantiation of template class 'std::__enforce_unordered_container_requirements<Identifier, std::hash<Identifier>, std::equal_to<Identifier>>' requested here
typename __enforce_unordered_container_requirements<_Key, _Hash, _Equal>::type
^
/Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk/usr/include/c++/v1/unordered_set:715:30: note: while substituting explicitly-specified template arguments into function template '__diagnose_unordered_container_requirements'
static_assert(sizeof(std::__diagnose_unordered_container_requirements<_Value, _Hash, _Pred>(0)), "");
^
/Users/test/src/Manager.cpp:7:43: note: in instantiation of member function 'std::unordered_set<Identifier>::~unordered_set' requested here
for(auto& requestIdentifier : module->getSupportedIdentifiers()){
@user12002570 user12002570 ссылка выше предполагает, что хэш-функция не является общедоступной, но, если я понимаю, это не так, поскольку она не находится внутри структуры, пожалуйста, поправьте меня, если это неправильное понимание.
Да, это не относится к вашему примеру.
Я не вижу в вашем коде никакой хеш-функции
@Yksisarvinen Я предоставил реализацию для booloperator==(const Identifier& lhs, const Identifier& rhs), разве этого недостаточно, а если нет, мне нужно реализовать дополнительный метод.
Опубликуйте минимально воспроизводимый пример . .
Это не имеет ничего общего с указателями или вашим дизайном Manager
или ModuleInterface
. Проблема сводится к следующему:
struct Identifier {
// side note: better to take args by value, and then std::move
Identifier(const std::string &d, const std::string &n,
const std::string &v)
: domain(d), name(n), version(v) {}
std::string domain;
std::string name;
std::string version;
auto operator<=>(const Identifier&) const = default; // to avoid boilerplate code, since C++20
};
// this is OK
std::map<Identifier, int> myMap; // value type doesn't really matter here
// this will not compile
std::unordered_set<Identifier> mySet;
Компаратором для ключей std::map
является std::less<Key>
, поэтому в вашем случае std::less<Identifier>
, что можно легко решить, установив по умолчанию оператор сравнения.
С другой стороны, std::unordered_set
нужен std::equal_to<Key>
(который у вас есть) и std::hash<Key>
(которого у вас нет). Один из простых способов сделать ваш Identifier
«хешируемым» — просто скопировать и вставить пример пользовательской специализации std::hash
, введенной в namespace std
из cppreference:
template<>
struct std::hash<Identifier>
{
std::size_t operator()(const Identifier& id) const noexcept
{
std::size_t h1 = std::hash<std::string>{}(id.domain);
std::size_t h2 = std::hash<std::string>{}(id.name);
std::size_t h3 = std::hash<std::string>{}(id.version);
return h1 ^ (h2 << 1) ^ (h3 << 2);
}
};
Примечание. Приведенный выше пример хэш-функции предназначен только для демонстрационных целей и может быть не очень эффективным.
Правильное объединение хешей — это отдельная наука; ваша реализация… не очень хороша. Несколько лучшие реализации можно найти здесь: stackoverflow.com/q/2590677/1968 … (к сожалению, это не так) сбивает с толку то, что C++ до сих пор не реализует частичную std::hash
специализацию для std::tuple
: в противном случае мы могли бы просто написать return std::hash<T>()(std::tie(id.domain, id.name, id.version));
(с подходящий T
, поскольку CTAD здесь использовать нельзя… грр.).
@KonradRudolph, да, хорошая мысль. Хэш примера подходит для демонстрации, но не для производства.
Примечание: я бы использовал умножение на некоторые относительные простые числа. (Но это компромисс между скоростью и возможными столкновениями в конечном итоге)
Или просто используйте Boost.ContainerHash на tuple<domain,name,version>
...