Я определил эту структуру класса шаблона:
template<typename T> struct Outer {
struct Inner { /* ...some stuff... */ };
};
Я хочу поставить Inner
объекты в unordered_map
(на самом деле, не непосредственно их, а контейнеры из них, поэтому подход к указанию объекта хеширования непосредственно в параметрах шаблона для unordered_map
не очень хорошая идея), поэтому я хотел специализироваться hash
класс для этих предметов.
Это не будет работать, потому что компилятор не может соответствовать Outer<T>::Inner
с типом, указанным при создании экземпляра hash
:
namespace std {
template<typename T> struct hash<typename Outer<T>::Inner > {
size_t operator ()( typename Outer<T>::Inner const & obj )
{ /* ...some stuff... */ }
};
};
Кто-нибудь знает решение этой проблемы?
Вы, очевидно, правы, что это не будет работать, потому что компилятор не может соответствовать специализации шаблона, основанной на таком зависимом типе (например, Inner может быть вложенной typedef, тогда как компилятор сможет определить разницу между этим типом происходит из вложенного typedef в Outer, в отличие от других? Это невозможно, это невозможно сказать).
Есть ряд решений.
Во-первых, вы можете переместить класс Inner за пределы класса Outer (и сделать их друзьями, если это необходимо). Вы также можете переместить его в пространство имен «detail» или скрыть его несколькими другими способами в зависимости от вашего контекста. Люди нередко избегают таких вложенных «внутренних» классов, потому что они могут вызвать ряд проблем, подобных этой, и у некоторых старых компиляторов даже возникают проблемы с приемом таких вложенных классов. Как правило, лучше переносить эти вложенные классы из класса Outer. С точки зрения фактического кода, вы делаете это:
template <typename T>
struct Outer; // forward-decl.
namespace detail {
template <typename T>
struct Outer_Inner {
friend class Outer<T>; // Optional
// ....
};
};
template <typename T>
struct Outer {
typedef detail::Outer_Inner<T> Inner;
friend class detail::Outer_Inner<T>; // Optional
// ...
};
namespace std {
template<typename T>
struct hash< detail::Outer_Inner<T> > {
// ..
};
};
Другое решение состоит в том, чтобы определить свой собственный класс хеширования, который вы можете дать unordered_set
, Как это:
template <typename T>
struct Outer {
struct Inner {
//..
};
struct InnerHash {
typedef Inner argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const& s) const {
return /* some hashing code */;
};
};
// ...
// An example unordered-set member:
std::unordered_set<Inner, InnerHash> m_set;
};
И, наконец, есть еще одно решение, о котором я могу подумать, что, как и первое, преимущество заключается в специализации std::hash
шаблон класса. Однако, это решение немного запутано, оно включает в себя обертывание вашего внутреннего класса во внешний шаблон класса, например так:
template <typename T>
struct InnerWrapper {
typedef typename Outer<T>::Inner value_type;
value_type data;
};
а затем создание специализации std::hash< InnerWrapper<T> >
, Это решение на самом деле имеет только то преимущество, что не навязчиво относится к существующей реализации класса Outer, но создает unordered_map
в этом случае означает, что карта должна содержать (прямо или косвенно) объекты InnerWrapper вместо непосредственного хранения внутренних объектов. Кроме того, вы должны заметить, что это решение можно смешать с первым решением, поскольку некоторые функции Inner, более тесно интегрированные с Outer, реализованы во вложенном классе, и более открытые функции Inner, реализованные снаружи. класс, таким образом избегая дружеских отношений и позволяя более тесную Внешне-Внутреннюю интеграцию, оставляя чистый пользовательский класс для доступа к функциональным возможностям Внутреннего.