STL 中一处方法的参数类型匹配错误
在写 vector 和 list 时都碰到了这个问题,最简单的两个 insert 操作,函数签名分别是:
// type1
template<InputIt>
iterator insert(Iterator pos, InputIt first, InputIt last);
// type2
iterator insert(Iterator pos, size_type count, const T& value);
但是,直接这样简单地在函数内部实现逻辑的话,会出现一种潜在问题,比如对于 vector,来说,如下调用会产生问题:
vector<int> vec;
vec.insert(vec.begin(), 12, 2);
会报错误,一种错误是
invalid type argument of unarg '*' (have 'int')
另一种是报 iterator_trait 那里的错误(为了不改动代码,我就不复现了)
两种错误本质都是编译期把原本是 int 类型的变量当指针使了。
问题的原因是 vec.insert(vec.begin(), 12, 2) 这句话被编译期识别成调用上面两个 insert 的 type1 而不是 type2。
原因是 size_type 是 size_t,也就是 unsigned int,而 T 是 int,而 12 和 2 两个参数传入的时候都被当做 int 了,因此 int 转成 size_type 需要一次隐式类型转换,而可以直接匹配上 type1 的模板,因为两个参数都是同类型的。
在进行调用的时候,匹配规则是:
- 严格类型匹配,比如上面调用代码如果改成:
vec.insert(vec.begin(), (size_t)12, 2);
那么就可以匹配上了;
2. 模板函数匹配成功,就是上面出现的情况
3. 隐式类型转换成功。
那么这个问题怎么解决呢,最简单的方法就是加一个 warpper 函数,然后判断进入 type1 的参数 first 和 last 是不是指针,标准库提供了 is_intergral 类就是解决这个问题的,将代码改成下面的即可:
template<class InputIt>
iterator __insert(iterator pos, InputIt first, last) {
return __insert_template(pos, first, last, typename std::is_integral<InputIt>::type());
}
template<class InputIt>
iterator __insert_template(iterator pos, InputIt first, last, std::true_type) {
return __insert(pos, size_type(first), T(last));
}
template<class InputIt>
iterator __insert_template(iterator pos, InputIt first, InputIt last, std::false_type) {
return __insert_aux(pos, first, last); // 真正的区间insert函数
}
其实当时自己已经知道是这个问题了,但是不知道怎么解决这个问题,看到了这篇文章才懂得:
http://www.voidcn.com/article/p-wacxdohk-ud.html
PS: 当时使用 gdb 调试了一下,验证变量类型,才知道 whatis 这个命令可以在 GDB 里看变量类型。