Если заменить *i
в цикле на **i
, возможно, вы получите нужный результат — но скорее всего, этого не произойдет. Да, строки будут выведены, но вероятность их следования в алфавитном порядке равна всего 1/24. Контейнер ssp
хранит свои элементы в отсортированном виде, однако он содержит указатели, поэтому сортироваться будут значения указателей, а не строки. Существует 24 возможных перестановки для четырех указателей, то есть 24 разных последовательности, из которых лишь одна отсортирована в алфавитном порядке [2] Строго говоря, не все 24 перестановки равновероятны, так что вероятность 1/24 не совсем точна. Тем не менее, остается бесспорный факт: существуют 24 разные перестановки, и вы можете получить любую из них.
.
Подходя к решению этой проблемы, нелишне вспомнить, что объявление
set ssp;
представляет собой сокращенную запись для объявления
setless> ssp;
Строго говоря, это сокращенная запись для объявления
set, allocator> ssp;
но в контексте данного совета распределители памяти несущественны.
Если вы хотите сохранить указатели string*
в контейнере set
так, чтобы их порядок определялся значениями строк, стандартный функтор сравнения less
вам не подойдет. Вместо этого необходимо написать собственный функтор сравнения, который получает указатели string*
и упорядочивает их по содержимому строк, на которые они ссылаются. Пример:
struct StringPtrLess:
public binary_function
const string*, // описан в совете 40
bool> {
bool operator() (const string *ps1, const string *ps2) const {
return *ps1 < *ps2:
}
};
После этого StringPtrLess
используется в качестве типа критерия сравнения ssp
:
typedef set StringPtrSet;
StringPtrSetssp; // Создать множество с объектами string
// и порядком сортировки, определяемым
// критерием StringPtrLess
// Вставить те же четыре строки
Теперь приведенный выше цикл будет работать именно так, как предполагалось (при условии, что ошибка была исправлена и вместо *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
const T& operator()(const T* ptr) const {
return *ptr;
}
};
transform(ssp.begin(), ssp.end(), // "Преобразовать" каждый
ostream.iterator(cout, "\n"), // элемент ssp посредством
Dereference()); // разыменования и записать
// результаты в cout
Впрочем, замена циклов алгоритмами будет подробно рассматриваться позднее, в совете 43. А сейчас речь идет о том, что при создании стандартного ассоциативного контейнера указателей следует помнить: содержимое контейнера будет сортироваться по значениям указателей. Вряд ли такой порядок сортировки вас устроит, поэтому почти всегда определяются классы-функторы, используемые в качестве типов сравнения.
Обратите внимание на термин «тип сравнения». Возможно, вас интересует, зачем возиться с созданием функтора вместо того, чтобы просто написать функцию сравнения для контейнера set
? Например, так:
bool stringPtrLess(const string* ps1, // Предполагаемая функция сравнения
const string* ps2) // для указателей string*,
{ // сортируемых по содержимому строки
return *ps1 < *ps2;
}
set ssp; // Попытка использования stringPtrLess
// в качестве функции сравнения ssp.
Читать дальше