C++:shared_ptr 作为 unordered_set 的键

发布于 2024-11-16 10:23:07 字数 546 浏览 8 评论 0原文

考虑下面的代码

#include <boost/unordered_set.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

int main()
{
    boost::unordered_set<int> s;
    s.insert(5);
    s.insert(5);
    // s.size() == 1 

    boost::unordered_set<boost::shared_ptr<int> > s2;
    s2.insert(boost::make_shared<int>(5));
    s2.insert(boost::make_shared<int>(5));
    // s2.size() == 2
}

问题是:为什么 s2 的大小是 2 而不是 1?我很确定它一定与哈希函数有关。我尝试查看 boost 文档并使用哈希函数,但没有运气。

有想法吗?

Consider the following code

#include <boost/unordered_set.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

int main()
{
    boost::unordered_set<int> s;
    s.insert(5);
    s.insert(5);
    // s.size() == 1 

    boost::unordered_set<boost::shared_ptr<int> > s2;
    s2.insert(boost::make_shared<int>(5));
    s2.insert(boost::make_shared<int>(5));
    // s2.size() == 2
}

The question is: how come the size of s2 is 2 instead of 1? I'm pretty sure it must have something to do with the hash function. I tried looking at the boost docs and playing around with the hash function without luck.

Ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

倾城°AllureLove 2024-11-23 10:23:08

请注意,在 Boost <= 1.46.0 中,boost::shared_ptr 的默认 hash_value 是其布尔值、true假。
对于任何不为 NULLshared_ptrhash_value 的计算结果为 1(一),因为 (bool)shared_ptr == true< /代码>。

换句话说,如果您使用 Boost <= 1.46.0,则将哈希集降级为链接列表

此问题已在 Boost 1.47.0 中修复,请参阅 https://svn.boost.org/trac /boost/ticket/5216

如果您使用 std::shared_ptr,请定义您自己的哈希函数,或使用 Boost >= 1.51.0 中的 boost/function/hash/extensions.hpp

Notice that in Boost <= 1.46.0, the default hash_value of a boost::shared_ptr is its boolean value, true or false.
For any shared_ptr that is not NULL, hash_value evaluates to 1 (one), as the (bool)shared_ptr == true.

In other words, you downgrade a hash set to a linked list if you are using Boost <= 1.46.0.

This is fixed in Boost 1.47.0, see https://svn.boost.org/trac/boost/ticket/5216 .

If you are using std::shared_ptr, please define your own hash function, or use boost/functional/hash/extensions.hpp from Boost >= 1.51.0

笑梦风尘 2024-11-23 10:23:08

正如您所发现的,插入到 s2 中的两个对象是不同的。

As you found out, the two objects inserted into s2 are distinct.

静水深流 2024-11-23 10:23:07

make_shared 分配一个新的 int,并在其周围包装一个 shared_ptr。这意味着您的两个 shared_ptr 指向不同的内存,并且由于您正在创建一个以指针值为键的哈希表,因此它们是不同的键。

出于同样的原因,这将导致大小为 2:

boost::unordered_set<int *> s3;
s3.insert(new int(5));
s3.insert(new int(5));
assert(s3.size() == 2);

在大多数情况下,您可以认为 shared_ptr 的作用就像指针一样,包括用于比较,但自动销毁除外。

不过,您可以定义自己的哈希函数和比较谓词,并将它们作为模板参数传递给 unordered_map: 但是

struct your_equality_predicate
    : std::binary_function<boost::shared_ptr<int>, boost::shared_ptr<int>, bool>
{
    bool operator()(boost::shared_ptr<int> i1, boost::shared_ptr<int> i2) const {
        return *i1 == *i2;
    }
};

struct your_hash_function
    : std::unary_function<boost::shared_ptr<int>, std::size_t>
{
    std::size_t operator()(boost::shared_ptr<int> x) const {
        return *x; // BAD hash function, replace with somethign better!
    }
};

boost::unordered_set<int, your_hash_function, your_equality_predicate> s4;

,由于以下几个原因,这可能是一个坏主意:

  1. 您会遇到令人困惑的情况,其中 x != ys4[x]s4[y] 是相同的。
  2. 如果有人更改了哈希键指向的值您的哈希将会崩溃!即:

    boost::shared_ptr; tmp(新 int(42));
    s4[tmp] = 42;
    *tmp = 24; // 未定义的行为
    

通常,对于哈希函数,您希望密钥不可变;无论以后发生什么,它总是比较相同的。如果您使用指针,通常希望指针标识是匹配的,如 extra_info_hash[&some_object] = ...;无论 some_object 的成员是什么,通常都会映射到相同的哈希值。由于键在插入后可变,因此实际执行此操作太容易了,从而导致哈希中出现未定义的行为。

make_shared allocates a new int, and wraps a shared_ptr around it. This means that your two shared_ptr<int>s point to different memory, and since you're creating a hash table keyed on pointer value, they are distinct keys.

For the same reason, this will result in a size of 2:

boost::unordered_set<int *> s3;
s3.insert(new int(5));
s3.insert(new int(5));
assert(s3.size() == 2);

For the most part you can consider shared_ptrs to act just like pointers, including for comparisons, except for the auto-destruction.

You could define your own hash function and comparison predicate, and pass them as template parameters to unordered_map, though:

struct your_equality_predicate
    : std::binary_function<boost::shared_ptr<int>, boost::shared_ptr<int>, bool>
{
    bool operator()(boost::shared_ptr<int> i1, boost::shared_ptr<int> i2) const {
        return *i1 == *i2;
    }
};

struct your_hash_function
    : std::unary_function<boost::shared_ptr<int>, std::size_t>
{
    std::size_t operator()(boost::shared_ptr<int> x) const {
        return *x; // BAD hash function, replace with somethign better!
    }
};

boost::unordered_set<int, your_hash_function, your_equality_predicate> s4;

However, this is probably a bad idea for a few reasons:

  1. You have the confusing situation where x != y but s4[x] and s4[y] are the same.
  2. If someone ever changes the value pointed-to by a hash key your hash will break! That is:

    boost::shared_ptr<int> tmp(new int(42));
    s4[tmp] = 42;
    *tmp = 24; // UNDEFINED BEHAVIOR
    

Typically with hash functions you want the key to be immutable; it will always compare the same, no matter what happens later. If you're using pointers, you usually want the pointer identity to be what is matched on, as in extra_info_hash[&some_object] = ...; this will normally always map to the same hash value whatever some_object's members may be. With the keys mutable after insertion is it all too easy to actually do so, resulting in undefined behavior in the hash.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文