Это связано с тем, что понятие строки C++ было обобщено до последовательности символов произвольного типа, обладающих произвольными характеристиками («traits») и хранящихся в памяти, выделенной произвольными распределителями. Все
string
– подобные объекты C++ в действительности являются специализациями шаблона
basic_string
, поэтому при диагностике ошибок, связанных с неверным использованием
string
, большинство компиляторов упоминает тип
basic_string
(некоторые компиляторы любезно включают в диагностику имя
string
, но большинство из них этого не делает). Нередко в диагностике указывается на принадлежность
basic_string
(а
также вспомогательных шаблонов
char_traits
и
allocator
) к пространству имен
std
, поэтому в сообщениях об ошибках, связанных с использованием string, нередко упоминается тип
не являются стандартными, но такова жизнь. Некоторые реализации STL отклоняются от Стандарта. Если вам не нравятся отклонения в текущей реализации STL, подумайте, не стоит ли перейти на другую реализацию. В совете 50 перечислены некоторые ресурсы, в которых можно найти альтернативные реализации.
Независимо от того, как тип
string
упоминается в диагностике компилятора, методика приведения диагностики к осмысленному минимуму остается той же: хитроумная конструкция с
basic_string
заменяется текстом «
string
». Если вы используете компилятор командной строки, задача обычно легко решается при помощи программы sed или сценарных языков типа Perl, Python или Ruby (пример сценария приведен в статье Золмана (Zolman) «An STL Error Message Decryptor for Visual C++» [26]). В приведенном примере производится глобальная замена фрагмента
std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >
строкой 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).
Рассмотрим пример более сложной диагностики. Предположим, вы реализуете программу для работы с электронной почтой, которая позволяет ссылаться на адресатов не только по адресам, но и по синонимам — скажем, адресу президента США (president@whitehouse.gov) ставится в соответствие синоним «The Big Cheese». В такой программе может использоваться ассоциативный контейнер для отображения синонимов на адреса электронной почты и функция
showEmailAddress
, которая возвращает адрес для заданного синонима:
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'