Чтение онлайн

ЖАНРЫ

Эффективное использование STL
Шрифт:

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 должна вернуть неконстантный итератор.

Взгляните еще раз. Да, объект

nicknames
объявлен как неконстантный тип
map
, но функция
showEmalAddress
является константной, а внутри константной функции все нестатические переменные класса становятся константными! Таким образом, внутри
showEmalAddress
объект
nicknames
является константным объектом
map
. Сообщение об ошибке внезапно обретает смысл. Мы пытаемся сгенерировать
iterator
для объекта
map
, который обещали не изменять. Чтобы исправить ошибку, необходимо либо привести
i
к типу
const_iterator
, либо объявить
showEmalAddress
неконстантной функцией. Вероятно, оба способа потребуют значительно меньших усилий, чем выяснение смысла сообщения об ошибке.

В этом совете были показаны некоторые текстовые подстановки, уменьшающие сложность сообщений об ошибках, но после непродолжительной практики вы сможете выполнять подстановки в голове. Я не музыкант, но мне рассказывали, что хорошие музыканты способны читать партитуру целиком, не присматриваясь к отдельным нотам. Опытные программисты STL приобретают аналогичные навыки. Они могут автоматически преобразовать конструкцию вида

std::basic_string<char, std::char_traits<char>, std::allocator<char> >
в
string
, нисколько не задумываясь над происходящим. Подобный навык разовьется и у вас, но до этих пор следует помнить, что диагностику компилятора почти всегда можно привести к вразумительному виду заменой длинных типов на базе шаблонов более короткими мнемоническими обозначениями. Во многих случаях для этого достаточно заменить расширенные определения типов именами, используемыми в программе. Именно это было сделано в приведенном примере, когда мы заменили
std::map<class string, class string, struct std::less<class string>, class std::allocator<class string> >
на
NicknameMap
.

Далее приведены некоторые рекомендации, которые помогут вам разобраться в сообщениях компилятора, относящихся к STL.

• Для контейнеров

vector
и
string
итераторы обычно представляют собой указатели, поэтому в случае ошибки с итератором в диагностике компилятора обычно указываются типы указателей. Например, если в исходном коде имеется ссылка на
vector<double>::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
).

• Итераторы вывода (например,

ostream_iterator
и
ostream_buf_iterator
— см. совет 29, а также итераторы, возвращаемые
back_inserter
,
front_inserter
и
inserter
) выполняют свои операции вывода или вставки внутри операторов присваивания, поэтому ошибки, относящиеся к этим типам итераторов, обычно приводят к появлению сообщений об ошибке внутри операторов присваивания, о которых вы и понятия не имеете. Чтобы понять, о чем идет речь, попробуйте откомпилировать следующий фрагмент:

vector<string*> v; // Попытка вывода содержимого

copy(v.begin, v.end, // контейнера указателей string*

ostream_iterator<string>(cout, "\n")); // как объектов string

• Если полученное сообщение об ошибке исходит из реализации алгоритма STL (то есть если код, в котором произошла ошибка, находится в

<algoritm>
), вероятно, проблема связана с типами, которые вы пытаетесь передать этому алгоритму. Пример — передача итераторов неправильной категории. Попробуйте откомпилировать следующий фрагмент:

Поделиться с друзьями: