lundi 25 mai 2015

Undefined behavior in std::unordered_set with custom predicate

I have an std::unordered_set that is supposed to store pointers to values stored in an std::list. The values are first added to the list, then their pointers are inserted into the set. The set uses a predicate that compares the values the pointers point to instead of the addresses. This produces undefined behavior.

Here's a minimal working example:

#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;
}

After running the program multiple times I get three possible outputs:

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

The output I expect is

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

with some lines like *a[c] == *b[c] true, depending on how many times the predicate is called.

I do not understand what results in undefined behavior. I get identical results with gcc4.8.2, gcc4.9.1, and gcc4.9.2.

Aucun commentaire:

Enregistrer un commentaire