STL笔记(5)条款49:学习破解有关STL的编译器诊断信息
2007-07-15 22:38
435 查看
条款49:学习破解有关STL的编译器诊断信息
用一个特定的大小定义一个vector是完全合法的,vector<int> v(10); // 建立一个大小为10的vector
而string在很多方面像vector,所以你可能希望可以这么做:
string s(10); // 常识建立一个大小为10的string
这不能编译。string没有带有一个int实参的构造函数。我的一个STL平台像这样告诉我那一点:
example.cpp(20): error C2664:'__thiscall std::basic_string<char, struct std::char_traits<char>,class std::allocator<char> >::std::basic_string<char, struct std::char_traits<char>,class std::allocator<char> >(const class std::allocator<char> &)': cannot convert parameter 1 from 'const int' to 'const class std::allocator<char> &' Reason: cannot convert from 'const int' to 'const class std::allocator<char> No constructor could take the source type, or constructor overload resolution was ambiguous
是不是很奇妙?消息的第一部分看起来像一只猫走过键盘,第二部分神秘地提到了一个从未在源代码中涉及的分配器,第三部分说构造函数调用是错的。当然,第三部分是准确的,但首先让我们关注于号称猫咪散步的结果上,因为当使用string时,这是你经常遇到的诊断信息的典型。
string不是一个类,它是typedef。实际上,它是这个的typedef:
basic_string<char, char_traits<char>, allocator<char> >
这是因为字符串的C++观念已经被泛化为表示带有任意字符特性(“traits”)的任意字符类型的序列并储存在以任意分配器分类的内存中。在C++里所有类似字符串的对象实际上都是basic_string模板的实例,这就是为什么当大多数编译器发出关于“程序错误使用string”的诊断信息时会涉及类型basic_string。(一些编译器很善良,在诊断信息中会使用string的名字,但大多数不会。)通常,那样的诊断信息会明确指出basic_string(以及服务助手模板char_traits和allocator)在std名字空间里,所以常常看到错误调用string会产生提及这种类型的诊断信息:
std::basic_string<char, std::char_traits<char>, std::allocator<char> >
这十分接近于上面编译器里使用的诊断信息,但不同的编译器使用这个主题的不同变体。我使用的另一个STL平台以这种方式表示string,
basic_string<char, string_char_traits<char>, __default_alloc_template<false,0> >
string_char_traits和__default_alloc_template的名字是非标准的,但是那是生活。一些STL实现背离了标准。如果你不喜欢你当前STL实现里的背离,考虑用另一个来替换它。条款50给了你可以找到可选择实现的位置的例子。
不管编译器诊断信息怎样表示string类型,把诊断信息减少到有意义的东西的技术是一样的:用文字“string”全局替换冗繁难解的basic_string。如果你使用的是命令行编译器,通常可以很容易地用一个类似sed的程序或一种脚本语言比如perl、python或ruby来完成。(你可以在Zolman的文章——《Visual C++的STL错误信息解码器》[26]——里找到一个这样的脚本的例子。)就上面的诊断信息而言,我们用string全局替换
std::basic_string<char, struct std::char_traits<char>, class std::allocator<char> >
可以得到这个:
example.cpp(20): error C2664:'__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构造函数形式。
顺便说一下,神秘地提到分配器的原因是每个标准容器都有一个只带有分配器的构造函数。就string而论,是三个可以用一个实参调用的构造函数之一,但由于某种原因,编译器指出带有分配器的那个是你试图调用的。编译器指错了,而诊断信息也令人误解。哦哟。
至于只带有分配器的构造函数,请不要使用它。那个构造函数是为了容易构造类型相同但分配器不等价的容器。通常,那是不好的,非常不好。要知道为什么,转向条款11。
现在让我们对付更富于挑战性的诊断信息。假定你正在实现一个允许用户通过昵称而不是电子邮件地址查找人的电子邮件程序。例如,这样的程序将可能使用“The Big Cheese”作为美国总统(碰巧是president@whitehouse.gov)电子邮件地址的同义词。这样的程序可以使用一个从昵称到电子邮件地址的映射,并可能提供一个成员函数showEmailAddress,显示和给定的昵称关联的电子邮件地址:
void NiftyEmailProgram::showEmailAddress(const string& nickname) const void NiftyEmailProgram::showEmailAddress(const string& nickname) const vector<string*> v; // 试图打印一个 copy(v.begin(), v.end(), // string*指针的容器, ostream_iterator<string>(cout, "\n")); // 被当作string对象
你得到一条源于STL算法实现内部的错误信息(即,源代码引发的错误在<algorithm>中),也许是你试图给那算法用的类型出错了。例如,你可能传了错误种类的迭代器。要看看这样的用法错误是怎样报告的,通过把这段代码喂给你的编译器来启发(并愉快!)自己:
list<int>::iterator i1, i2; // 把双向迭代器 sort(i1, i2); // 传给一个需要 // 随机访问迭代器的算法
你使用常见的STL组件比如vector、string或for_each算法,而编译器说不知道你在说什么,你也许没有#include一个需要的头文件。正如条款48的解释,这问题会降临在长期以来都可以顺利编译而刚移植到新平台的代码。
相关文章推荐
- STL笔记(5)条款49:学习破解有关STL的编译器诊断信息
- STL笔记(5)条款49:学习破解有关STL的编译器诊断信息
- Effective STL学习笔记-条款32
- Effective STL学习笔记-条款21
- Effective STL学习笔记-条款13|14
- Effective STL学习笔记-条款7
- Tomcat学习笔记 - 错误日志 - NetBeans配置tomcat出错情况总结 -- 尚未授予访问 Tomcat 服务器的权限。请在服务器管理器的 Tomcat 定制器中设置 "manager-script" 角色的正确用户名和口令。 有关详细信息, 请查看服务器日志。
- Effective STL学习笔记-条款20
- Effective STL学习笔记-条款40
- effective stl 第49条:学会分析与STL相关的编译器诊断信息
- EffectiveC++学习笔记-条款49
- Effective C++学习笔记 条款06:如不想使用编译器自动生成的函数,就该明确拒绝
- Effective STL学习笔记-条款38
- Effective STL学习笔记-条款33
- LTE 212 PUCCH 控制信息的信道编码和PUSCH without UL-SCH data信道编码学习笔记
- STL学习笔记5— —容器list
- STL学习笔记-容器
- C++ STL 学习笔记 priority_queue
- 《营销管理,第11版》第五章学习笔记——收集信息和测量市场分析
- C++ 学习笔记之 STL 队列