Can a QPointer be the key to a std::map

1.1k views Asked by At

According to SGI's doc on associative containers, "Since elements are stored according to their keys, it is essential that the key associated with each element is immutable". I sometimes use a pointer as a key to std::map, since, although the pointed object might be mutable, the pointer itself is constant.

QPointer is technically an object mimicking a pointer, and Qt's doc says that we can use QPointers exactly like pointers. Since the QPointer object itself may change during execution, can it still be used as the key to a std::map container?

Edit 1 : I can't use a QMap, I have to stick to std::map.
Edit 2 : The code compiles when I use a QPointer. The question is about whether I should expect unpleasant surprises at runtime.

3

There are 3 answers

0
aschepler On BEST ANSWER

No, this is not safe, because QPointer<T> may change to NULL when a QObject is destroyed. (QPointer is similar to std::weak_ptr.) So this code would produce Undefined Behavior:

class CopyableWidget : public QWidget {
  Q_OBJECT;
public:
  CopyableWidget(Widget* parent = 0) : QWidget(parent) {}
  CopyableWidget(const CopyableWidget& w, Widget* parent = 0) : QWidget(parent) {}
};

std::vector<CopyableWidget> v(2); // Two default-constructed widgets
std::map<QPointer<CopyableWidget>, int> wid_values;
wid_values[&v[0]] = 1;
wid_values[&v[1]] = 2;
// wid_values contains { {&v[0], 1}, {&v[1], 2} }
v.resize(1);
// The QPointer in wid_values constructed from &v[1] now acts like NULL.
// wid_values contains { {&v[0], 1}, {NULL, 2} }
// But &v[0] > NULL (on most platforms). Class invariant was violated!
0
Fred Foo On

I take it (from the principle of least surprise) that a QPointer won't "magically" change where it points if you don't reassign it and don't delete the underlying object, so it should be safe.

(I must add that I've seen Qt violate the principle of least surprise several times, so test to be sure.)

4
Brad On

It is undefined behavior to compare pointers from different allocation blocks for anything other than equality/inequality. I.e. After allocating a block with new or malloc, you can walk through the buffer and test if your pointer is less than the end of the buffer. But you can't do two separate news/mallocs and compare the resulting pointers for anything other than equality/inequality.

Well... you can but you may not like the results.