++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, // "Преобразовать" каждый