Впрочем, не все так просто. Хотя элементы set/multiset и не являются неизменными, реализации могут предотвратить их возможную модификацию. Например, оператор* для set
Законны ли такие реализации? Может, да, а может — нет. По этому вопросу Стандарт высказывается недостаточно четко, и в соответствии с законом Мерфи разные авторы интерпретируют его по-разному. В результате достаточно часто встречаются реализации STL, в которых следующий фрагмент компилироваться не будет (хотя ранее говорилось о том, что он успешно компилируется):
EmplIDSet se;// Контейнер set объектов
// Employee, упорядоченных
// по коду
Employee selectedID; // Объект работника с заданным кодом
EmpIDSet::iterator=se.find(selectedID);
if (i!=se.end()){
i->setTitle("Corporate Deity"); // Некоторые реализации STL
}; // выдают ошибку в этой строке
Вследствие неоднозначности стандарта и обусловленных ею различий в реализациях программы, пытающиеся модифицировать элементы контейнеров set и multiset, не переносимы.
Что из этого следует? К счастью, ничего особенно сложного:
•если переносимость вас не интересует, если вы хотите изменить значение элемента в контейнере set/multiset и ваша реализация STL это разрешает — действуйте. Помните о том, что ключевая часть элемента (то есть часть элемента, определяющая порядок сортировки элементов в контейнере) должна сохраниться без изменений;
•если программа должна быть переносимой, элементы контейнеров set/ multiset модифицироваться не могут (по крайней мере, без преобразования const_cast).
Кстати, о преобразованиях. Вы убедились в том, что изменение вторичных данных элемента set/multiset может быть вполне оправданно, поэтому я склонен показать, как это делается — а точнее, делается правильно и переносимо. Сделать это нетрудно, но при этом приходится учитывать тонкость, о которой забывают многие программисты — преобразование должно приводить к
EmpIDSet::iterator i=se.find(selectedID);
if (i!=se.end()) {
i->setTitle("Corporate Deity"); // Некоторые реализации STL
}// выдают ошибку в этой строке,
// поскольку *i имеет атрибут const
Чтобы этот фрагмент нормально компилировался и работал, необходимо устранить константность *i. Правильный способ выглядит так:
if (i!=se.end()){// Устранить
const_cast
}
Мы берем объект, на который ссылается
Многие программисты пытаются воспользоваться следующим кодом:
if (i!=se.end()){// Преобразовать *i
static_cast
}
Приведенный фрагмент эквивалентен следующему:
if (i!=se.end()){// То же самое.
((Employee)(*i)).setTitle("Corporate Deity");
// но с использованием
} // синтаксиса С
Оба фрагмента компилируются, но вследствие эквивалентности работают неправильно. На стадии выполнения объект
if (i!=se.end()){
Employee tempCopy(*i);// Скопировать *i в tempCopy
tempCopy.setTitle("Corporate Deity");// Изменить tempCopy
}