Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

Сказанное прежде всего касается контейнеров

map
и
multimap
, поскольку программы, пытающиеся изменить значение ключа в этих контейнерах, не будут компилироваться:

map<int, string> m;

m.begin->first = 10; // Ошибка! Изменение ключей

// в контейнере map запрещено

multimap<int, string> mm;

mm.begin->first = 20; // Ошибка! Изменение ключей

// в контейнере multimap запрещено

Дело в том, что элементы объекта типа

map<K, V>
или
multimap<K, V>
относятся к типу
pair<const K, V>
. Ключ относится к типу
const K
и поэтому не может изменяться. Впрочем, его все же можно изменить с применением
const_cast
, как показано ниже. Хотите — верьте, хотите — нет, но иногда это даже нужно.

Обратите внимание: в заголовке этого совета ничего не сказано о контейнерах 

map
и
multimap
. Для этого есть веские причины. Как показывает предыдущий пример, модификация ключа «на месте» невозможна для
map
и
multimap
(без применения преобразования
const_cast
), но может быть допустима для
set
и
multiset
. Для объектов типа
set<T>
и
multiset<T>
в контейнере хранятся элементы типа
T
, а не
const T
. Следовательно, элементы контейнеров
set
и mu
l
tiset можно изменять в любое время, и преобразование
const_cast
для этого не требуется (вообще говоря, дело обстоит не так просто, но не будем забегать вперед).

Сначала выясним, почему элементы

set
и
multiset
не имеют атрибута
const
. Допустим, у нас имеется класс
Employee
:

class Employee {

public:

 …

 const string& name const; // Возвращает имя работника

 void setName(const string& name); // Задает имя работника

 const string& title const; // Возвращает должность

 void setTitle(const string& title); // Задает должность

 int idNumber const; // Возвращает код работника

 …

}

Объект содержит разнообразные сведения о работнике. Каждому работнику назначается уникальный код, возвращаемый функцией

idNumber
. При создании контейнера
set
с объектами
Employee
было бы вполне разумно упорядочить его по кодам работников:

struct IDNumberLess:

 public binary_function<Employee, Employee, bool> { // См. совет 40

 bool operator (const Employees lhs, const Employees rhs) const {

return lhs.idNumber < rhs. IdNumber;

 }

}

typedef set<Employee, IDNumberLess> EmplIDSet;

EmplIDSet se; // Контейнер set объектов

// Employee, упорядоченных

// по коду

С практической точки зрения код работника является ключомдля элементов данного множества, а остальные данные вторичны. Учитывая это обстоятельство, ничто не мешает перевести работника на более интересную должность. Пример:

Employee selectedID; //
Объект работника с заданным кодом

EmpIDSet::iterator = se.find(selectedID);

if (i!=se.end) {

 i->setTitle("Corporate Deity"); // Изменить должность

}

Поскольку мы всего лишь изменяем вторичный атрибут данных, не влияющий на порядок сортировки набора, этот фрагмент не приведет к порче данных, и он вполне допустим.

Спрашивается, почему нельзя применить ту же логику к ключам контейнеров

map
и
multimap
? Почему бы не создать контейнер
map
, ассоциирующий работников со страной, в которой они живут; контейнер с функцией сравнения
IDNumberLess
, как в предыдущем примере? И почему бы в таком контейнере не изменить должность без изменения кода работника, как в предыдущем примере?

Откровенно говоря, мне это кажется вполне логичным, однако мое личное мнение в данном случае несущественно. Важно то, что Комитет по стандартизации решил, что ключи

map/multimap
должны быть неизменными (
const
), а значения
set/multiset
— не должны.

Значения в контейнерах

set/multiset
не являются неизменными, поэтому попытки их изменения обычно нормально компилируются. Данный совет лишь напоминает вам о том, что при модификации элементов
set/multiset
не следует изменять ключевую часть (то есть ту часть элемента, которая влияет на порядок сортировки в контейнере). В противном случае целостность данных контейнера будет нарушена, операции с контейнером начнут приводить к непредсказуемым результатам, и все это произойдет по вашей вине. С другой стороны, это ограничение относится только к ключевым атрибутам объектов, содержащихся в контейнере. Остальные атрибуты объектов находятся в вашем полном распоряжении — изменяйте на здоровье!

Впрочем, не все так просто. Хотя элементы

set/multiset
и не являются неизменными, реализации могут предотвратить их возможную модификацию. Например, оператор
*
для
set<T>::iterator
может возвращать
const T&
, то есть результат разыменования итератора
set
может быть ссылкой на
const
элемент контейнера! При такой реализации изменение элементов
set
и
multiset
невозможно, поскольку при любом обращении к элементу автоматически добавляется объявление
const
.

Законны ли такие реализации? Может, да, а может — нет. По этому вопросу Стандарт высказывается недостаточно четко, и в соответствии с законом Мерфи разные авторы интерпретируют его по-разному. В результате достаточно часто встречаются реализации STL, в которых следующий фрагмент компилироваться не будет (хотя ранее говорилось о том, что он успешно компилируется):

EmplIDSet se; // Контейнер set объектов

// Employee, упорядоченных

// по коду

Employee selectedID; // Объект работника с заданным кодом

EmpIDSet::iterator = se.find(selectedID);

if (i!=se.end) {

 i->setTitle("Corporate Deity"); // Некоторые реализации STL

}; // выдают ошибку в этой строке

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

set
и
multiset
, не переносимы.

Поделиться:
Популярные книги

Ученичество. Книга 1

Понарошку Евгений
1. Государственный маг
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Ученичество. Книга 1

Идеальный мир для Лекаря 7

Сапфир Олег
7. Лекарь
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 7

Обыкновенные ведьмы средней полосы

Шах Ольга
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Обыкновенные ведьмы средней полосы

Треск штанов

Ланцов Михаил Алексеевич
6. Сын Петра
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Треск штанов

Медиум

Злобин Михаил
1. О чем молчат могилы
Фантастика:
фэнтези
7.90
рейтинг книги
Медиум

"Фантастика 2023-123". Компиляция. Книги 1-25

Харников Александр Петрович
Фантастика 2023. Компиляция
Фантастика:
боевая фантастика
альтернативная история
5.00
рейтинг книги
Фантастика 2023-123. Компиляция. Книги 1-25

Гром над Тверью

Машуков Тимур
1. Гром над миром
Фантастика:
боевая фантастика
5.89
рейтинг книги
Гром над Тверью

Идеальный мир для Лекаря 11

Сапфир Олег
11. Лекарь
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 11

Совок

Агарев Вадим
1. Совок
Фантастика:
фэнтези
детективная фантастика
попаданцы
8.13
рейтинг книги
Совок

Табу на вожделение. Мечта профессора

Сладкова Людмила Викторовна
4. Яд первой любви
Любовные романы:
современные любовные романы
5.58
рейтинг книги
Табу на вожделение. Мечта профессора

Сонный лекарь 4

Голд Джон
4. Не вывожу
Фантастика:
альтернативная история
аниме
5.00
рейтинг книги
Сонный лекарь 4

Огненный князь 4

Машуков Тимур
4. Багряный восход
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Огненный князь 4

Войны Наследников

Тарс Элиан
9. Десять Принцев Российской Империи
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Войны Наследников

Бестужев. Служба Государевой Безопасности. Книга вторая

Измайлов Сергей
2. Граф Бестужев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга вторая