【问题标题】:Undefined behavior in std::unordered_set with custom predicate带有自定义谓词的 std::unordered_set 中的未定义行为
【发布时间】:2015-05-26 05:39:36
【问题描述】:

我有一个std::unordered_set,它应该存储指向存储在std::list 中的值的指针。这些值首先被添加到列表中,然后它们的指针被插入到集合中。该集合使用一个谓词来比较指针指向的值而不是地址。这会产生未定义的行为。

这是一个最小的工作示例:

#include <unordered_set>
#include <iostream>
#include <string>
#include <list>

using namespace std;

template<typename T> struct set_hash {
  size_t operator()(const T* p) const noexcept {
    return reinterpret_cast<uintptr_t>(p);
  }
};
template<typename T> struct set_eq {
  bool operator()(const T* a, const T* b) const noexcept {
    std::cout << "*a["<<*a<<"] == *b["<<*b<<"] "
              << boolalpha << (*a == *b) << std::endl;
    return *a == *b;
  }
};

template<typename T> using set_t =
  std::unordered_set<const T*, set_hash<T>, set_eq<T>>;

int main()
{
  set_t<string> set;
  list<string> list{"a", "b", "a", "c", "a", "d"};

  for (auto& str : list) {
    set.insert(&str);
    cout << str << ' ';
  }
  cout << endl;

  for (auto p : set) cout << *p << ' ';
  cout << endl;

  string c("c");
  cout << **set.find(&c) << endl;

  return 0;
}

多次运行程序后,我得到三个可能的输出:

a b a c a d 
d a c a b a 
Segmentation fault

a b a c a d 
d a c a b a 
*a[c] == *b[a] false
Segmentation fault

a b a c a d 
d a c a b a 
*a[c] == *b[c] true
c

我期望的输出是

a b a c a d 
a b c (not necessarily in this order)
c

*a[c] == *b[c] true 这样的行,取决于谓词被调用的次数。

我不明白是什么导致了未定义的行为。 我使用 gcc4.8.2、gcc4.9.1 和 gcc4.9.2 得到相同的结果。

【问题讨论】:

  • 您使用指针是否有特殊原因?在 C++ 中,很少需要再使用指针了。
  • 您在集合中获得重复条目的原因是因为您使用指针作为哈希。您添加到集合中的指针都是不同的,因此散列函数将为每个指针返回不同的值。
  • @JoachimPileborg 是的。我在写一些更复杂的东西时遇到了这种行为,但这与问题不太相关。
  • 您要打破的不变量是,可能比较为相等字符串的元素可能具有不同的值(因为它们可以使用不同的指针)。
  • 如果我知道不会从列表中删除任何内容,那么使用指针作为哈希有什么问题。列表不稳定吗?

标签: c++ c++11 hash predicate unordered-set


【解决方案1】:

问题在于哈希函数对指针进行哈希处理,而比较函数则比较指针指向的值。因此,如果您有两个具有相同值的不同列表元素,哈希函数将给出不同的哈希值(来自指针),而比较函数将比较相等。

散列函数需要保持一致——它需要散列值而不是指针。

【讨论】:

    猜你喜欢
    • 2020-03-15
    • 2013-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多