Теперь приведенный выше цикл будет работать именно так, как предполагалось (при условии, что ошибка была исправлена и вместо
*i
используется
**i
).
for (StringPtrSet::const_iterator i = ssp.begin;
i != ssp.end; // Порядок вывода:
++i) // "Anteater", "Lemur",
cout << **i << endl; // "Penguin", "Wombat"
Если
вы предпочитаете использовать алгоритм, напишите функцию, которая разыменовывает указатели
string*
перед выводом, а затем используйте ее в сочетании с
for_each
:
void print(const string *ps) // Вывести в cout объект,
{ // на который ссылается ps
cout << *ps << endl;
}
for_each(ssp.begin, ssp.end, print); // Вызвать print для каждого
// элемента ssp
Существует более изощренное решение — обобщенный функтор разыменования, используемый с
transform
и
ostream_iterator
:
// Функтор получает T* и возвращает const T&
struct Dereference {
template<typename T>
const T& operator(const T* ptr) const {
return *ptr;
}
};
transform(ssp.begin, ssp.end, // "Преобразовать" каждый
ostream.iterator<string>(cout, "\n"), // элемент ssp посредством
Dereference); // разыменования и записать
// результаты в cout
Впрочем, замена циклов алгоритмами будет подробно рассматриваться позднее, в совете 43. А сейчас речь идет о том, что при создании стандартного ассоциативного контейнера указателей следует помнить: содержимое контейнера будет сортироваться по значениям указателей. Вряд ли такой порядок сортировки вас устроит, поэтому почти всегда определяются классы-функторы, используемые в качестве типов сравнения.
Обратите внимание на термин «тип сравнения». Возможно, вас интересует, зачем возиться с созданием функтора вместо того, чтобы просто написать функцию сравнения для контейнера
set
? Например, так:
bool stringPtrLess(const string* ps1, // Предполагаемая функция сравнения
const string* ps2) // для указателей string*,
{ // сортируемых по содержимому строки
return *ps1 < *ps2;
}
set<string, stringPtrLess> ssp; // Попытка использования stringPtrLess
// в качестве функции сравнения ssp.
//
Не компилируется!!!
Проблема заключается в том, что каждый из трех параметров шаблона
set
должен быть типом. К сожалению,
stringPtrLess
— не тип, а функция, поэтому попытка задать
stringPtrLess
в качестве функции сравнения
set
не компилируется. Контейнеру
set
не нужна функция; ему нужен тип, на основании которого можно создатьфункцию.
Каждый раз, когда вы создаете ассоциативный контейнер указателей, помните о том, что вам, возможно, придется задать тип сравнения контейнера. В большинстве случаев тип сравнения сводится к разыменованию указателя и сравнению объектов, как это сделано в приведенном выше примере
StringPtrLess
. Шаблон для таких функторов сравнения стоит держать под рукой. Пример:
struct DereferenceLess {
template <typename PtrType>
bool operator(PtrType pT1, // Параметры передаются по значению.
PtrType рТ2) const // поскольку они должны быть
{ // указателями (или по крайней мере
return *рТ1 < *рТ2; // вести себя, как указатели)
}
};
Данный шаблон снимает необходимость в написании таких классов, как
StringPtrLess
, поскольку вместо них можно использовать
DereferenceLess
:
set<string*, DereferenceLess> ssp; // Ведет себя так же, как
// set<string*, stringPtrLess>
И последнее замечание. Данный совет посвящен ассоциативным контейнерам указателей, но он в равной степени относится и к контейнерам объектов, которые ведут себя как указатели (например, умные указатели и итераторы). Если у вас имеется ассоциативный контейнер умных указателей или итераторов, подумайте, не стоит ли задать тип сравнения и для него. К счастью, решение, приведенное для указателей, работает и для объектов-аналогов. Если определение
DereferenceLess
подходит в качестве типа сравнения для ассоциативного контейнера
T*
, оно с большой вероятностью подойдет и для контейнеров итераторов и умных указателей на объекты
T
.
Совет 21. Следите за тем, чтобы функции сравнения возвращали false в случае равенства
Сейчас я покажу вам нечто любопытное. Создайте контейнер
set
с типом сравнения
less_equal
и вставьте в него число 10:
set<int, less_equal<int> > s; // Контейнер s сортируется по критерию "<="