Скотт Мейерс - Эффективное использование STL
- Название:Эффективное использование STL
- Автор:
- Жанр:
- Издательство:Питер
- Год:2002
- Город:СПб.
- ISBN:5-94723-382-7
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Скотт Мейерс - Эффективное использование STL краткое содержание
В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.
Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.
Эффективное использование STL - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
No constructor could take the source type, or constructor overload resolution was ambiguous
Уже лучше. Осталось каких-нибудь 745 символов, можно начинать разбираться в сообщении. В глаза бросается упоминание шаблона std::_Tree
. В Стандарте ничего не сказано о шаблоне с именем Tree
, но мы помним, что имена, начинающиеся с символа подчеркивания и прописной буквы, зарезервированы для авторов реализаций. Перед нами один из внутренних шаблонов, используемых в реализации некой составляющей STL.
Оказывается, практически во всех реализациях STL стандартные ассоциативные контейнеры ( set
, multiset
, map
и multimap
) строятся на основе базовых шаблонов. По аналогии с тем, как при использовании string в диагностике упоминается тип basic_string
, при работе со стандартными ассоциативными контейнерами часто выдаются сообщения с упоминанием базовых шаблонов. В данном примере этот шаблон называется _Tree
, но в других известных мне реализациях встречались имена tree
и _rb_tree
, причем в последнем имени отражен факт использования красно-черных (Red-Black) деревьев, самой распространенной разновидности сбалансированных деревьев, встречающейся в реализациях STL.
В приведенном выше сообщении упоминается знакомый тип std::map, class std::allocator >
. Перед нами тип используемого контейнера map
, если не считать типов функции сравнения и распределителя памяти (которые не были заданы при определении контейнера). Сообщение об ошибке станет более понятным, если заменить этот тип нашим вспомогательным определением NicknameMap
. Результат:
example.срр(17):error С2440:'initalzing': cannot convert from 'class std::_Tree, struct NicknameMap::_Kfn, struct std::less, class std::allocator >::const_iterator' to 'class std::_Tree, struct NicknameMap_Kfn, struct std::less, class std::allocator >: iterator'
No constructor could take the source type, or constructor overload resolution was ambiguous
Сообщение стало короче, но его смысл остался туманным; нужно что-то сделать с _Tree
. Известно, что шаблон _Tree
зависит от реализации, поэтому узнать смысл его параметров можно только одним способом — чтением исходных текстов. Но зачем копаться в исходных текстах реализации STL, если это не нужно? Попробуем просто заменить все данные, передаваемые Tree
, условным обозначением «НЕЧТО» и посмотрим, что из этого выйдет. Результат:
example.cpp(17):error С2440:'initalizing': cannot convert from 'class std::_Tree<���НЕЧТО::const_iterator' to 'class std::_Tree<���НЕЧТО:iterator'
No constructor could take the source type, or constructor overload resolution was ambiguous
А вот с этим уже можно работать. Компилятор жалуется на попытку преобразования const_iterator
в iterator
с явным нарушением правил константности.
Вернемся к исходному примеру; строка, вызвавшая гнев компилятора, выделена жирным шрифтом:
class NiftyEmailProgram {
private:
typedef map NicknameMap;
NicknameMap nicknames;
public:
void showEmailAddress(const string& nickname) const;
};
void NiftyEmailProgram::showEmailAddress(const string& nickname) const {
NicknameMap::iterator i = nicknames.find(nickname);
if (i != nicknames.end())…
}
Сообщение об ошибке можно истолковать лишь одним разумным образом — мы пытаемся инициализировать переменную i
(типа iterator
) значением типа const_iterator
, возвращаемым при вызове map::find
. Такая интерпретация выглядит несколько странно, поскольку find
вызывается для объекта nicknames
. Объект nicknames
не является константным, поэтому функция find должна вернуть неконстантный итератор.
Взгляните еще раз. Да, объект nicknames
объявлен как неконстантный тип map
, но функция showEmalAddress
является константной, а внутри константной функции все нестатические переменные класса становятся константными! Таким образом, внутри showEmalAddress
объект nicknames
является константным объектом map
. Сообщение об ошибке внезапно обретает смысл. Мы пытаемся сгенерировать iterator
для объекта map
, который обещали не изменять. Чтобы исправить ошибку, необходимо либо привести i
к типу const_iterator
, либо объявить showEmalAddress
неконстантной функцией. Вероятно, оба способа потребуют значительно меньших усилий, чем выяснение смысла сообщения об ошибке.
В этом совете были показаны некоторые текстовые подстановки, уменьшающие сложность сообщений об ошибках, но после непродолжительной практики вы сможете выполнять подстановки в голове. Я не музыкант, но мне рассказывали, что хорошие музыканты способны читать партитуру целиком, не присматриваясь к отдельным нотам. Опытные программисты STL приобретают аналогичные навыки. Они могут автоматически преобразовать конструкцию вида std::basic_string, std::allocator >
в string
, нисколько не задумываясь над происходящим. Подобный навык разовьется и у вас, но до этих пор следует помнить, что диагностику компилятора почти всегда можно привести к вразумительному виду заменой длинных типов на базе шаблонов более короткими мнемоническими обозначениями. Во многих случаях для этого достаточно заменить расширенные определения типов именами, используемыми в программе. Именно это было сделано в приведенном примере, когда мы заменили std::map, class std::allocator >
на NicknameMap
.
Далее приведены некоторые рекомендации, которые помогут вам разобраться в сообщениях компилятора, относящихся к STL.
• Для контейнеров vector
и string
итераторы обычно представляют собой указатели, поэтому в случае ошибки с итератором в диагностике компилятора обычно указываются типы указателей. Например, если в исходном коде имеется ссылка на vector::iterator
, в сообщении почти наверняка будет упоминаться указатель double*
. Исключение составляет реализация STLport в отладочном режиме; в этом случае итераторы vector
и string
не являются указателями. За информацией о STLport и отладочном режиме обращайтесь к совету 50.
• Сообщения, в которых упоминаются back_insert_iterator
, front_insert_iterator
и insert_iterator
, почти всегда означают, что ошибка была допущена при вызове back_inserter
, front_inserter
или inserter
соответственно ( back_inserter
возвращает объект типа back_insert_iterator
, front_inserter
возвращает объект типа front_insert_iterator
, a inserter
возвращает объект типа insert_iterator
; за информацией об этих типах обращайтесь к совету 30). Если эти функции не вызывались в программе, значит, они были вызваны из других функций (косвенно или явно).
• Сообщения с упоминаниями binder1st
и binder2nd
обычно свидетельствуют об ошибке при использовании bind1st
и bind2nd
( bind1st
возвращает объект типа binder1st
, a bind2nd
возвращает объект типа binder2nd
).
Интервал:
Закладка: