++i) // "Anteater", "Lemur"
cout << *i << endl; // "Penguin", "Wombat"
Однако на практике ничего похожего не происходит. Вместо строк выводятся четыре шестнадцатеричных числа — значения указателей. Поскольку в контейнере set
хранятся указатели, *i
является не строкой, а copy
:
copy(ssp.begin, ssp.end, // Скопировать строки.
ostream_iterator
//(не компилируется!)
не только делает программу более компактной, но и помогает быстрее обнаружить ошибку, поскольку вызов copy
не компилируется. Итератор ostream_iterator
должен знать тип выводимого объекта, поэтому когда компилятор обнаруживает расхождение между заданным в параметре шаблона типом string
и типом объекта, хранящегося в ssp(string*)
, он выдает ошибку. Еще один довод в пользу сильной типизации…
Если заменить *i
в цикле на **i
, ssp
хранит свои элементы в отсортированном виде, однако он содержит указатели, поэтому сортироваться будут
Подходя к решению этой проблемы, нелишне вспомнить, что объявление
set
представляет собой сокращенную запись для объявления
set
Строго говоря, это сокращенная запись для объявления
set
но в контексте данного совета распределители памяти несущественны.
Если вы хотите сохранить указатели 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 ssp; // Создать множество с объектами 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, // "Преобразовать" каждый