что происходит, когда вы изменяете элемент std :: set? - PullRequest
12 голосов
/ 26 мая 2009

Если я изменяю элемент std :: set, например, через итератор, я знаю, что он не «вставлен заново» или «восстановлен», но есть ли упоминания о том, что он вызывает неопределенное поведение? Например, я бы предположил, что вставки будут облажаться. Есть ли упоминание о том, что конкретно происходит?

Ответы [ 3 ]

24 голосов
/ 26 мая 2009

Вы не должны редактировать значения, сохраненные в наборе напрямую. Я скопировал это из документации MSDN, которая является несколько авторитетной:

Используется набор классов контейнеров STL для хранения и поиска данных из коллекции, в которой значения из содержащихся элементов являются уникальными и служить в качестве ключевых значений в соответствии к которому данные автоматически приказал. Значение элемента в набор не может быть изменен напрямую. Вместо этого вы должны удалить старые значения и вставьте элементы с новыми значениями.

Почему это довольно легко понять. Реализация set не будет знать, что вы изменили значение за ее спиной. Обычная реализация - красно-черное дерево. Изменив значение, позиция в дереве для этого экземпляра будет неправильной. Вы ожидаете увидеть все виды неправильного поведения, например, exists запросов, возвращающих неверный результат из-за поиска, идущего вниз по неверной ветви дерева.

5 голосов
/ 26 мая 2009

Точный ответ зависит от платформы, но, как правило, «ключ» (материал, который вы кладете в набор или первый тип карты), считается «неизменным». Проще говоря, это не должно быть изменено, и нет такой вещи, как автоматическая повторная вставка.

Точнее, переменные-члены , используемые для сравнения ключа, изменять нельзя.

Компилятор Windows vc довольно гибкий (протестирован с VC8), и этот код компилируется:

// creation
std::set<int> toto;
toto.insert(4);
toto.insert(40);
toto.insert(25);

// bad modif
(*toto.begin())=100;

// output
for(std::set<int>::iterator it = toto.begin(); it != toto.end(); ++it)
{
    std::cout<<*it<<" ";
}
std::cout<<std::endl;

Вывод 100 25 40 , который явно не отсортирован ... Плохо ... Тем не менее, такое поведение полезно, когда вы хотите изменить данные, не участвующие в операторе <</em>. Но вам лучше знать, что вы делаете: это цена, которую вы получаете за слишком гибкий подход.

Некоторые могут предпочесть поведение gcc (протестировано с 3.4.4), которое выдает ошибку «назначение местоположения только для чтения». Вы можете обойти это с const_cast:

const_cast<int&>(*toto.begin())=100;

Это теперь компилируется и на gcc, тот же вывод: 100 25 40 . Но, по крайней мере, это, вероятно, заставит вас задуматься о том, что происходит, а затем перейдите к переполнению стека и увидите этот поток: -)

4 голосов
/ 26 мая 2009

Вы не можете сделать это; они const. Не существует метода, с помощью которого set может обнаружить, что вы вносите изменение во внутренний элемент, и в результате вы не можете этого сделать. Вместо этого вы должны удалить и снова вставить элемент. Если вы используете элементы, которые дорого копировать, вам, возможно, придется переключиться на использование указателей и пользовательских компараторов (или переключиться на компилятор C ++ 1x, который поддерживает ссылки на rvalue, что сделало бы все намного приятнее).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...