简体   繁体   English

为模板类中的嵌套类专门化std :: hash

[英]Specializing std::hash for nested class in a template class

I have a template class Baz which contains a nested class Sub . 我有一个模板类Baz ,其中包含一个嵌套类Sub I'd like to define a hash function for this subclass by specializing std::hash. 我想通过专门化std :: hash来为此子类定义一个哈希函数。 However, it doesn't seem to work. 但是,它似乎不起作用。

#include <functional>

struct Foo {
    struct Sub {
    };
};

template <class T>
struct Bar {
};

template <class T>
struct Baz {
    struct Sub {
        int x;
    };
};

// declare hash for Foo::Sub - all right
namespace std {
    template <>
    struct hash< Foo::Sub >;
}

// declare hash for Bar<T> - all right
namespace std {
    template <class T>
    struct hash< Bar<T> >;
}

// declare hash function for Baz<T>::Sub - doesn't work!
namespace std {
    template <class T>
    struct hash< Baz<T>::Sub >;
}

// Adding typename produces a different error.
namespace std {
    template <class T>
    struct hash< typename Baz<T>::Sub >;
}

Gcc 4.5.3 complains: Gcc 4.5.3抱怨:

$ g++ -std=c++0x -c hash.cpp
hash.cpp:34:30: error: type/value mismatch at argument 1 in template parameter list for ‘template<class _Tp> struct std::hash’
hash.cpp:34:30: error:   expected a type, got ‘Baz<T>::Sub’
hash.cpp:40:12: error: template parameters not used in partial specialization:
hash.cpp:40:12: error:         ‘T’

UPDATE 更新

What I'm really trying to do is implement a container which supports stable references (not in the C++ sense) to elements within it. 我真正想做的是实现一个容器,该容器支持其中的元素的稳定引用 (不是C ++的意义)。 I want to allow the user to insert these references into std::unordered_set and similar, and use them to access or modify existing elements efficiently. 我想允许用户将这些引用插入到std::unordered_set等中,并使用它们有效地访问或修改现有元素。 The following is just a mockup, not the exact container I'm implementing. 以下只是一个模型,并不是我正在实现的确切容器。 The problem is in defining a hash function for the reference type. 问题在于为引用类型定义哈希函数。

template <class T>
class Container {
public:
    class Reference {
    public:
        // operator==, operator!=, operator< ...., isNull()
    private:
        size_t index; // index into m_entries (or could be anything else)
        // possibly more stuff
    };

    Reference insert (const T &value);
    Reference find (const T &value);
    void remove (Reference r);
    Reference first ();
    Reference next (Reference prev);

private:
    struct Entry { T value, ... };

    std::vector<Entry> m_entries;
};

Just pull the Reference class out of Container. 只需将Reference类从Container中拉出即可。

template <class Container>
class Reference {
public:
    typedef typename Container::value_type value_type; // etc...

    // operator==, operator!=, operator< ...., isNull()
private:
    size_t index; // index into m_entries (or could be anything else)
    // possibly more stuff
};

template <class T>
class Container {
public:
    typedef ::Reference<Container> Reference;
    friend class Reference; // If you cannot help it

    typedef T value_type;

    Reference insert (const T &value);
    Reference find (const T &value);
    void remove (Reference r);
    Reference first ();
    Reference next (Reference prev);

private:
    struct Entry { T value, ... };

    std::vector<Entry> m_entries;
};

Specialize like this: 像这样专门化:

namespace std {
    template <typename Container>
    struct hash<Reference<Container>>;
}

The answer to this question is that what you're trying to do is simply impossible to do. 这个问题的答案是,您试图做的事根本不可能做。 The compiler cannot figure out what outer class contains a subtype. 编译器无法确定哪个外部类包含子类型。 Consider why: 考虑为什么:

struct outer1 { typedef int Sub; };
struct outer2 { typedef int Sub; };

How is the compiler supposed to figure out which outer you want when it gets a Sub? 编译器在获得Sub时应该如何确定您想要哪个外部对象? It cannot. 这不可以。 There's no possible way for this to work. 没有可行的方法。

In your case it could be remotely possible, though extremely difficult, to derive IFF Sub dependend upon T. But this would require the compiler to know where Sub comes from and it just doesn't. 在您的情况下,尽管很困难,但有可能远程导出T依赖的IFF Sub。但是,这将要求编译器知道Sub来自何处,而实际上却不是。

So you simply cannot do this. 因此,您根本无法做到这一点。 No way, no how. 没办法,没有办法。

If you need some generic approach to finding a hash function for your types then you'll need to make a metafunction get_hash. 如果您需要某种通用方法来为您的类型查找哈希函数,则需要创建一个元函数get_hash。 It could look for a "hash_type" internal typedef by default and be overridden for the standard hashes. 默认情况下,它可能会寻找“ hash_type”内部typedef并为标准哈希值覆盖。 Lot of typing... 很多打字...

Alternatively, put Sub out of the containing class as its own template and have a typedef there instead of inner class. 或者,将Sub从包含类中移出作为其自己的模板,并在那里具有typedef而不是内部类。 Then you can specialize hash on your template. 然后,您可以在模板上专门化哈希。

Otherwise, just supply the hash you know you need to the hash function parameter to the template you're using. 否则,只需将您需要的哈希值提供给要使用的模板的hash函数参数即可。

You cannot specialize a template on a dependent type like that. 您不能像这样依赖类型专门化模板。 I would try something like this. 我会尝试这样的事情。

struct yes {};

template <typename T>
yes accept_baz_sub(typename Baz<T>::Sub&&);
void accept_baz_sub(...);

template <class T>
struct is_baz_sub : std::is_same<decltype(accept_baz_sub(std::declval<T>())), yes>
{};

template <typename BazSub>
using CheckedBazSub = typename std::enable_if<is_baz_sub<BazSub>::value, BazSub>::type;

namespace std {
    template <class BazSub>
    struct hash<CheckedBazSub<BazSub>>;
}

Edit: This doesn't quite work, according to [temp.alias] §14.5.7.2 an alias template name is never deduced. 编辑:这并不完全有效,根据[temp.alias]§14.5.7.2,永远不会推断出别名模板名称。

Another solution would be to write your own hash function object and let the container use that (third template parameter of std::unordered_map for instance). 另一种解决方案是编写自己的哈希函数对象,然后让容器使用该对象(例如std::unordered_map第三个模板参数)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM