++i)                                              // "Anteater", "Lemur"

 cout << *i << endl;                               // "Penguin", "Wombat"

Однако на практике ничего похожего не происходит. Вместо строк выводятся четыре шестнадцатеричных числа — значения указателей. Поскольку в контейнере set хранятся указатели, *i является не строкой, а указателем на строку. Пусть этот урок напоминает, чтобы вы следовали рекомендациям совета 43 и избегали написания собственных циклов. Использование алгоритма copy:

copy(ssp.begin, ssp.end,           // Скопировать строки.

 ostream_iterator(cout,"\n")); //содержащиеся в ssp, в cout

                                       //(не компилируется!)

не только делает программу более компактной, но и помогает быстрее обнаружить ошибку, поскольку вызов copy не компилируется. Итератор ostream_iterator должен знать тип выводимого объекта, поэтому когда компилятор обнаруживает расхождение между заданным в параметре шаблона типом string и типом объекта, хранящегося в ssp(string*), он выдает ошибку. Еще один довод в пользу сильной типизации…

Если заменить *i в цикле на **i, возможно, вы получите нужный результат — но скорее всего, этого не произойдет. Да, строки будут выведены, но вероятность их следования в алфавитном порядке равна всего 1/24. Контейнер ssp хранит свои элементы в отсортированном виде, однако он содержит указатели, поэтому сортироваться будут значения указателей, а не строки. Существует 24 возможных перестановки для четырех указателей, то есть 24 разных последовательности, из которых лишь одна отсортирована в алфавитном порядке[2].

Подходя к решению этой проблемы, нелишне вспомнить, что объявление

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;

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

Перейти на страницу:

Похожие книги