KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Скотт Мейерс - Эффективное использование STL

Скотт Мейерс - Эффективное использование STL

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Скотт Мейерс, "Эффективное использование STL" бесплатно, без регистрации.
Перейти на страницу:

строкой string, в результате чего будет получено следующее сообщение:

example.срр(20):error С2664:'))thiscall string::string(const class std::allocator<char>&)': cannot convert parameter 1 from 'const int' to 'const class std::allocator<char>&'

Из этого сообщения можно понять, что проблема связана с типом параметра, переданного конструктору string. Несмотря на загадочное упоминание allocator<char>, вам не составит труда просмотреть различные формы конструкторов string и убедиться в том, что ни одна из этих форм не вызывается только с аргументом размера.

Кстати, упоминание распределителя памяти (allocator) связано с наличием у всех стандартных контейнеров конструктора, которому передается только распределитель памяти. У типа string существуют три одноаргументных конструктора, но компилятор по какой-то причине решает, что вы пытаетесь передать именно распределитель. Его предположение ошибочно, а диагностика лишь сбивает с толку.

Что касается конструктора, получающего только распределитель памяти, — пожалуйста, не используйте его; он слишком часто приводит к появлению однотипных контейнеров с неэквивалентными распределителями памяти. Как правило, такая ситуация крайне нежелательна (более подробные объяснения приведены в совете 11).

Рассмотрим пример более сложной диагностики. Предположим, вы реализуете программу для работы с электронной почтой, которая позволяет ссылаться на адресатов не только по адресам, но и по синонимам — скажем, адресу президента США ( [email protected]) ставится в соответствие синоним «The Big Cheese». В такой программе может использоваться ассоциативный контейнер для отображения синонимов на адреса электронной почты и функция showEmailAddress, которая возвращает адрес для заданного синонима:

class NiftyEmailProgram {

private:

 typedef map<string, string> NicknameMap;

 NicknameMap nicknames;

public:

 void showEmailAddress(const string& nickname) const;

};

В реализации showEmailAddress потребуется найти адрес электронной почты, ассоциированный с заданным синонимом. Для этого может быть предложен следующий вариант:

void NiftyEmail Program::showEmailAddress(const string& nickname) const {

 NicknameMap::iterator i = nicknames.find(nickname);

 if (i !=nicknames.end())…

}

Компилятору такое решение не понравится. На то есть веская, но не очевидная причина. Чтобы помочь вам разобраться в происходящем, одна из платформ STL услужливо выдает следующее сообщение:

example.cpp(17):error С2440:'initializing': cannot convert from 'class std::_Tree<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >, struct std::pair<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > const, class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > >, struct std::map<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >, class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >, struct std::less<class std::basc_string<char, struct std::char_traits<char>, class std::allocator<char> > >, class std::allocator<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > > >::_Kfn, struct std::less<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > >, class std::allocator<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > > >::const_iterator' to 'class std::_Tree<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >, struct std::pair<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > const, class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > >, struct std::map<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >, class std: std::char_traits<char>, class std::allocator<char> >, struct std std::basic_string<char, struct std::char_traits<char>, class std::basic_string<char, struct std::less<class std::allocator<char> >, struct std::less<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > >, class std::allocator<class std::char traits<char>, class std::allocator<char> : basic_string<char, struct : _Kfn, struct std::less<class std::<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >, struct std::char_traits<char>, class std::basic_string<char, struct std::pair<class std::basic_string<char, struct allocator<char> > const, class std::char_traits<char>, class std::allocator<char> >, struct std::map<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >, class std std::allocator<char> >, struct std::char_traits<char>, class std::basic_string<char, struct std::_Kfn, struct std::less<class std::allocator<char> > >, class std::basic_string<char, struct std::char_traits<char>, class less<class std::basic_string<char, struct allocator<char> > >, class std::char_traits<char>, class std::basic_string<char, struct std::allocator<class allocator<char> > > >::char traits<char>, class std::allocator<class std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> > > >::iterator'

No constructor could take the source type, or constructor overload resolution was ambiguous

Сообщение состоит из 2095 символов и выглядит довольно устрашающе, но я видал и похуже. Например, одна из моих любимых платформ STL однажды выдала сообщение из 4812 символов. Наверное, вы уже догадались, что я люблю ее совсем не за это.

Давайте немного сократим эту хаотическую запись и приведем ее к более удобному виду. Начнем с замены конструкции basic_string… на string. Результат выглядит так:

example.cpp(17):error С2440:'initializing': cannot convert from 'class std::_Tree<class string, struct std::pair<class string const, class string >, struct std::map<class string, class string, struct std::less<class string>, class std::allocator<class string > >::_Kfn, struct std::less<class string>, class std::allocator<class string> >::const_iterator' to 'class std::_Tree<class string, struct std::pair<class string const, class string>, struct std::map<class string, class string, struct std::less<class string>, class std::allocator<class string> >::_Kfn, struct std::less<class string>, class std::allocator<class string> >: iterator'

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 string, class string, stuct std::less<class string>, class std::allocator<class string> >. Перед нами тип используемого контейнера map, если не считать типов функции сравнения и распределителя памяти (которые не были заданы при определении контейнера). Сообщение об ошибке станет более понятным, если заменить этот тип нашим вспомогательным определением NicknameMap. Результат:

example.срр(17):error С2440:'initalzing': cannot convert from 'class std::_Tree<class string, struct std::pair<class string const, class string >, struct NicknameMap::_Kfn, struct std::less<class string>, class std::allocator<class string > >::const_iterator' to 'class std::_Tree<class string, struct std::pair<class string const, class string>, struct NicknameMap_Kfn, struct std::less<class string>, class std::allocator<class string> >: 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<string, string> 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 должна вернуть неконстантный итератор.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*