【经验】实现STL算法时遇到的模板编译错误问题
在实现set_union算法时调用了自己写的copy算法,出现了以下问题。
Error 1 error C2665: 'xyz_stl::__copy' : none of the 2 overloads could convert all the argument types
即“__copy的2个重载不能转换所有的参数类型”。
注:__copy是照着STL源码剖析进行命名的(实际上我倒很不喜欢SGI的那么长的命名),是针对输入迭代器(InputIterator)的迭代器类型(iterator_category)进行两个分支,如果仅仅是普通的输入迭代器,就采用迭代器比较(first != last)的方式判断是否到达末尾,如果是随机访问迭代器(RandomAccessIterator),则用一个int来递减通过判断该int是否大于0来判断是否到达末尾。
看看错误信息的输出
e:\code\数据结构\stl_algo\stl_algo\stl_algo.h(230): could be 'OutputIterator xyz_stl::__copy<InputIterator,OutputIterator>(RandomAccessIterator,RandomAccessIterator,OutputIterator,std::random_access_iterator_tag)'
1> with
1> [
1> OutputIterator=std::ostream_iterator<int,char,std::char_traits<char>>
1> , InputIterator=std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<int>>>
1> , RandomAccessIterator=std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<int>>>
1> ]
1> e:\code\数据结构\stl_algo\stl_algo\stl_algo.h(220): or 'OutputIterator xyz_stl::__copy<InputIterator,OutputIterator>(InputIterator,InputIterator,OutputIterator,std::input_iterator_tag)'
1> with
1> [
1> OutputIterator=std::ostream_iterator<int,char,std::char_traits<char>>
1> , InputIterator=std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<int>>>
1> ]
相当于给出了更详细的说明,但是这个说明我没理解清楚(因为太长了没仔细看),对我的调试产生了很严重的误导……
我以为是“既可能匹配到input_iterator_tag也可能匹配到random_access_iterator_tag,分别给出了这两种情况下的模板参数”。然后就很混乱,甚至想出了用个函数返回iterator_category*来进行强制转换,虽然这样改了后编译通过,但是根据之前写distance的经验,并不需要这么做,只有对value_type等其他几个迭代器属性时才需要返回指针。
PS:这段出错信息就可以看出来我的输入迭代器是std::set的迭代器(树常迭代器),输出迭代器是输出流迭代器,模板的名字太长orz
后来仔细查看调用__copy函数的代码
OutputIterator operator()(InputIterator first, InputIterator last,
OutputIterator result)
{
typedef typename
std::iterator_traits<OutputIterator>::iterator_category
iter_category;
return __copy(first, last, result, iter_category());
}
在一堆长长的名字里找bug真是得看眼力orz问题出在iter_category是取得输出迭代器)的迭代器类型,而这里的输出迭代器是ostream_iterator,它继承自类_Outit(微软STL中),而这个类的定义如下
// base for output iterators
typedef iterator<output_iterator_tag, void, void, void, void> _Outit;
迭代器类型是output_iterator_tag,由于我只对input_iterator_tag和random_access_iterator_tag进行了“强化”(就是编译期的if语句),所以无法找到匹配这种迭代器类型的函数模板。实际上也不能为输出迭代器写copy,这种情况只会出现在代码错误的时候,比如我这里。
那么回到出错信息,后面还有一段
while trying to match the argument list ... with ... with ...
而前面那一段错误信息实际上是 could be ... with ... with ...
也就是在尝试匹配参数列表时遇到了问题,不是可能匹配两种模板,而是在匹配这两种模板时出了问题,编译器提示说重载个数不够。
其实还有个一直疑问的问题,那就是我在实现跟STL一模一样的名字时,我用了自己的命名空间xyz_stl,但是在命名空间内部如果直接调用该函数时会出现错误信息
error C2668: 'xyz_stl::copy' : ambiguous call to overloaded function
我之前百思不得其解,明明没有使用using namespace std来污染命名空间,为什么会出现有歧义的调用呢?
后来想起来了,函数模板是在用确定类型调用它时才实例化。
而我在调用这个函数模板时,该cpp文件使用了using namespace std;而且函数模板是直接声明为inline的,也就是像宏一样直接展开代码。
虽然还是理解不太清楚,但是大概就是这两个原因吧- -
——————————————更新——————————————
今天在实现find_end函数,在进行测试时出现了问题。在list中查找一段vector的出现位置。
auto it2 = xyz_stl::find_end(lst.cbegin(), lst.cend(),
v0.cbegin(), v0.cend());
// 错误输出如下:
>e:\code\数据结构\stl_algo\stl_algo\stl_algo.h(): error C2782: 'FwIt1 xyz_stl::search(FwIt1,FwIt2,FwIt2,FwIt2)' : template parameter 'FwIt2' is ambiguous
> e:\code\数据结构\stl_algo\stl_algo\stl_algo.h() : see declaration of 'xyz_stl::search'
> could be 'std::reverse_iterator<std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<int>>>>'
> or 'reviter1'
> e:\code\数据结构\stl_algo\stl_algo\stl_algo.h() : see reference to function template instantiation 'BidIt1 xyz_stl::__find_end<FwIt1,FwIt2>(BidIt1,BidIt1,BidIt2,BidIt2,std::bidirectional_iterator_tag,std::bidirectional_iterator_tag)' being compiled
> with
> [
> BidIt1=std::_List_const_iterator<std::_List_val<std::_List_simple_types<int>>>
> , FwIt1=std::_List_const_iterator<std::_List_val<std::_List_simple_types<int>>>
> , FwIt2=std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<int>>>
> , BidIt2=std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<int>>>
> ]
> e:\code\数据结构\stl_algo\stl_algo\teststlalgo.cpp() : see reference to function template instantiation 'FwIt1 xyz_stl::find_end<std::_List_const_iterator<std::_List_val<std::_List_simple_types<int>>>,std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<int>>>>(FwIt1,FwIt1,FwIt2,FwIt2)' being compiled
> with
> [
> FwIt1=std::_List_const_iterator<std::_List_val<std::_List_simple_types<int>>>
> , FwIt2=std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<int>>>
> ]
又是长长的错误信息,但是VS确实厉害,有了上次浪费宝物VS瞎纠结的教训,一共三句错误代码,分别双击,可以来到4个错误位置。
reviter1 rresult = xyz_stl::search(reviter1(last1), rlast1,
reviter2(last2), rlast2);
注:reviter1和reviter2是分别针对FwIt1、FwIt2的2个逆向迭代器的别名。
template <typename FwIt1, typename FwIt2>
inline FwIt1 search(FwIt1 first1, FwIt2 last1, FwIt2 first2, FwIt2 last2)
return __find_end(first1, last1, first2, last2,
category1(), category2());
auto it2 = xyz_stl::find_end(lst.cbegin(), lst.cend(),
v0.cbegin(), v0.cend());
模板参数是从4条语句中最下面一条往上推测的,而with后面[]中的内容则是模板参数推测,回到刚才长长的错误信息中,从下往上看:
第4句推测出了FwIt1和FwIt2;
第3句推测出了BidIt1和BidIt2;
第2句推测失败:既可能是vector的常迭代器,也可能是reviter1(BidIt1的逆向迭代器),
那么问题出在第二句,为什么会推测出2种可能呢?再看看第2句的定位处:
template <typename FwIt1, typename FwIt2>
inline FwIt1 search(FwIt1 first1, FwIt2 last1, FwIt2 first2, FwIt2 last2)
……
……
……
好吧,我的last1是FwIt2,first1是FwIt1,而实际上first1和last1是同一种迭代器类型(vector的),first2和last2也是同一种迭代器类型(list的)。
所以,仅仅只是写错一个数字……就出现了这么大一坨shit一样的恶心错误信息……还是这么简单的功能实现……
简直是坑死人不偿命的模板元编程……
【经验】实现STL算法时遇到的模板编译错误问题的更多相关文章
- stm32的hall库新建模板编译错误: #error "Please select first the target STM32F1xx device used in your application (in stm32f1xx.h file)"的处理
在stm32f1xx.h file文件中找到如下代码: /* Uncomment the line below according to the target STM32L device used i ...
- C++标准模板库STL算法与自适应容器(栈和队列)
参考<21天学通C++>第23与第24章节,对STL算法与自适应容器进行介绍. 实际上在前面的STL顺序容器.关联容器进行介绍时或多或少引用到了一些STL算法中的模板函数.而自适应容器是在 ...
- STL算法与树结构模板
STL算法 STL 算法是一些模板函数,提供了相当多的有用算法和操作,从简单如for_each(遍历)到复杂如stable_sort(稳定排序),头文件是:#include <algorithm ...
- 【转】三十分钟学会STL算法
转载自: http://net.pku.edu.cn/~yhf/UsingSTL.htm 这是本小人书.原名是<using stl>,不知道是谁写的.不过我倒觉得很有趣,所以化了两个晚上把 ...
- STL学习系列之一——标准模板库STL介绍
库是一系列程序组件的集合,他们可以在不同的程序中重复使用.C++语言按照传统的习惯,提供了由各种各样的函数组成的库,用于完成诸如输入/输出.数学计算等功能. 1. STL介绍 标准模板库STL是当今每 ...
- 7.9 C++ STL算法
参考:http://www.weixueyuan.net/view/6406.html 总结: STL提供了大量操作容器的算法,这些算法大致可以分为:排序.搜索.集合运算.数值处理和拷贝等,这些算法的 ...
- C++11 STL算法简介
STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库.它被容纳于C++标准程序库(C++ Standard Library)中,是ANS ...
- STL——算法简介
一.算法概观 以有限的步骤,解决逻辑或数学上的问题,这一专门科目我们称为算法.特定的算法往往搭配特定的数据结构,例如binary search tree(二叉搜索树)和 RB-tree 便是为了解决查 ...
- C++ 11 STL算法
STL算法部分主要由头文件<algorithm>,<numeric>,<functional>组成.要使用 STL中的算法函数必须包含头文件<algorith ...
随机推荐
- Reverse a String
题目: 翻转字符串 先把字符串转化成数组,再借助数组的reverse方法翻转数组顺序,最后把数组转化成字符串. 你的结果必须得是一个字符串 这是一些对你有帮助的资源: Global String Ob ...
- [VS]VS快捷键
VS快速跳到某一行:CTRL+G VS鼠标移动到下一个高亮处:Ctrl+Shift+上下箭头 VS转到定义后返回:Ctrl+- VS折叠全部代码:Ctrl.M+Ctrl.O VS代码格式化:Ctrl. ...
- Apache .htaccess文件
今天在将ThinkPHP的URL模式由普通模式(URL_MODE=1)http://localhost/mythinkphp/index.php/Index/user/id/1.html改为重写模式 ...
- 深入理解Eureka之源码解析
转载请标明出处: http://blog.csdn.net/forezp/article/details/73017664 本文出自方志朋的博客 Eureka的一些概念 Register:服务注册 当 ...
- liunx工具学习之taskset
当你优化多线程任务的时候,发现某个线程在不同的核上跳转较大,从而耗费CPU的时候想法肯定是想可以把对应线程绑定到特定的核上,可是每次这样操作每次尝试看效果的时候都要重启进程,那有没有一个工具可以直接处 ...
- JAVA多线程----用--死锁
(1) 互斥条件:一个资源每次只能被一个进程使用.(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放.(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺.(4) ...
- Hibernate 一对一 (one-to-one)
一对一(one-to-one)实例(Person-IdCard) 一对一的关系在数据库中表示为主外关系.例如.人和身份证的关系.每个人都对应一个身份证号.我们应该两个表.一个是关于人信息的表(Pers ...
- IIS反向代理/Rewrite/https卸载配置
目标,使IIS具有类似与Nginx的功能,将指定域名的请求重定向到IIS内.IIS外.其他机器上的其他端口,并且实现https卸载功能 重点预告: 1.安装最新版urlrewrite(微软开发的)插件 ...
- 20179223《Linux内核原理与解析》第六周学习笔记
视频知识学习 给MenuOS增加time和time-asm命令 1.更新menu代码到最新版 2.再main()函数中增加MenuConfig 3.增加对应的Time函数和TimeAsm函数(这里的函 ...
- tableau-详细级别表达式——2、阵列分析
tableau做阵列分析 合作时间越长的客户对销售额的贡献越大吗? 下面的视图按照客户首次购买的年份将客户分组,以便对比各个阵列的年度销售贡献额. 每个客户的最早订单日期将体现出首次购买日期.不过,由 ...