您的位置:首页 > 其它

STL笔记(5)条款49:学习破解有关STL的编译器诊断信息

2014-12-12 23:27 253 查看

STL笔记(5)条款49:学习破解有关STL的编译器诊断信息

条款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的解释,这问题会降临在长期以来都可以顺利编译而刚移植到新平台的代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: