Может ли QPointer быть ключом к std::map

Согласно документу SGI об ассоциативных контейнерах, "поскольку элементы хранятся в соответствии с их ключами, важно, чтобы ключ, связанный с каждым элементом, был неизменным". Иногда я использую указатель в качестве ключа для std:: map, поскольку, хотя указанный объект может быть изменяемым, сам указатель является постоянным.

Технически QPointer - это объект, имитирующий указатель, и документация Qt говорит, что мы можем использовать QPointers точно так же, как указатели. Поскольку сам объект QPointer может измениться во время выполнения, может ли он по-прежнему использоваться в качестве ключа для контейнера std:: map?

Редактировать 1: я не могу использовать QMap, я должен придерживаться std::map.
Редактировать 2: код компилируется, когда я использую QPointer. Вопрос в том, стоит ли ожидать неприятных сюрпризов во время выполнения.

3 ответа

Решение

Нет, это не безопасно, потому что QPointer<T> может измениться на NULL когда QObject уничтожен (QPointer похож на std::weak_ptr.) Таким образом, этот код будет производить неопределенное поведение:

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!

Это неопределенное поведение - сравнивать указатели из разных блоков выделения для чего-либо, кроме равенства / неравенства. Т.е. после выделения блока с помощью new или malloc, вы можете пройти через буфер и проверить, меньше ли ваш указатель, чем конец буфера. Но вы не можете сделать две отдельные новости / malloc и сравнить полученные указатели для чего-либо, кроме равенства / неравенства.

Ну... вы можете, но вам могут не понравиться результаты.

Я понимаю (из принципа наименьшего удивления), что QPointer не будет "волшебным образом" менять то, куда он указывает, если вы не переназначаете его и не удаляете базовый объект, поэтому он должен быть безопасным.

(Должен добавить, что я видел, как Qt несколько раз нарушал принцип наименьшего удивления, так что проверяйте, чтобы быть уверенным.)

Другие вопросы по тегам