Может ли повторная вставка элемента повторно проверить итераторы?

У меня есть некоторые объекты в unordered_set, которые в какой-то момент необходимо обновить таким образом, чтобы не изменить поведение функции хеширования или оператора сравнения. Разделение структуры на карту или наличие изменяемого поля сейчас не вариант. Я придумал решение:

struct X { int x, y; };
// operator== and hash defined using only x
std::unordered_set<X> mySet;
// insert a bunch of stuff...
mySet.emplace(1, 2); // y contains 2

// get an iterator and a reference to the element
auto it = mySet.find(X{ 1, 3 }); // (y field here doesn't matter)
const X& ref = *it; // get a pointer to the element
std::cout << ref.y << '\n'; // 2
std::cout << it->y << '\n'; // 2

// now i want to change y to 4
mySet.erase(it);
mySet.emplace_hint(it, 1, 4); // y now contains 4
std::cout << ref.y << '\n'; // 4 or UB?
std::cout << it->y << '\n'; // 4 or UB?

Это прекрасно компилируется и работает с clang, но я не уверен, что стандарт позволяет мне это делать, поскольку стирание делает недействительным итератор, в котором был стерт элемент. Используя метод insert() с одним и тем же значением (с подсказкой итератора или без нее), гарантируется, что новый элемент будет сохранен в той же ячейке памяти, таким образом "повторно проверяя" итераторы и ссылки?


person Taylor Hansen    schedule 05.03.2018    source источник
comment
Это прекрасно компилируется и работает с clang -- С включенной оптимизацией и без?   -  person PaulMcKenzie    schedule 05.03.2018
comment
@PaulMcKenzie Работает как с оптимизацией, так и без нее.   -  person Taylor Hansen    schedule 05.03.2018
comment
как насчет предоставления X средства доступа mutable к его частям без хеширования?   -  person Kerrek SB    schedule 05.03.2018
comment
В C++17 вы можете использовать механизм extract, чтобы получить сам узел из контейнера, изменить элемент, а затем вернуть узел обратно.   -  person Kerrek SB    schedule 05.03.2018
comment
Если изменение не изменяет хэш, нельзя ли использовать для этого std::swap?   -  person super    schedule 05.03.2018
comment
Если вы освобождаете блок памяти, а затем сразу же запрашиваете новый точно такого же размера, для диспетчера памяти не будет необоснованным вернуть вам тот же блок обратно. Но уж точно никаких гарантий.   -  person Bo Persson    schedule 05.03.2018


Ответы (2)


Нет, это поведение undefined, и это просто удача, что оно работает.

Подумайте, что произойдет, если реализация unordered_set изменит размер контейнера после вашего вызова erase.

person druckermanly    schedule 05.03.2018

После того, как итератор был признан недействительным; Это оно. Вы больше не можете заставить его волшебным образом указывать на правильную вещь. Что правильно? То, на что он ранее указывал; вещь, которая теперь сидит на том месте, где она была?

Если вы не сделаете итераторы недействительными; тогда вы никогда не коснетесь неопределенного поведения; но использование недопустимого итератора - это UB.

person UKMonkey    schedule 05.03.2018