Оказывается, практически во всех реализациях STL стандартные ассоциативные контейнеры (set
, multiset
, map
и multimap
) строятся на основе базовых шаблонов. По аналогии с тем, как при использовании string в диагностике упоминается тип basic_string
, при работе со стандартными ассоциативными контейнерами часто выдаются сообщения с упоминанием базовых шаблонов. В данном примере этот шаблон называется _Tree
, но в других известных мне реализациях встречались имена tree
и _rb_tree
, причем в последнем имени отражен факт использования красно-черных (Red-Black) деревьев, самой распространенной разновидности сбалансированных деревьев, встречающейся в реализациях STL.
В приведенном выше сообщении упоминается знакомый тип std::map
. Перед нами тип используемого контейнера map
, если не считать типов функции сравнения и распределителя памяти (которые не были заданы при определении контейнера). Сообщение об ошибке станет более понятным, если заменить этот тип нашим вспомогательным определением NicknameMap
. Результат:
example.срр(17):error С2440:'initalzing': cannot convert from 'class std::_Tree
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 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
неконстантной функцией. Вероятно, оба способа потребуют значительно меньших усилий, чем выяснение смысла сообщения об ошибке.