在vs2017进行泛型模板编程的一些笔记
前言
我周末用c++,写数据结构的作业,出于装逼炫技(这两个词好像没差)的目的,我用oop的思想,和**现代c++**的语法来写作业。
其中我很自然的使用了泛型加上一些一些工程技巧。
而很遗憾的的是,当中出现了非常多的一些问题,一些是由于我对于c的学习还不是很熟练,另一些则是因为编译器对于modern c支持得不好,或者vs对它支持的不好。
模板类中的模板类中的子类
读上去好像有点绕口,我们举个栗子
template<class T>
class O
{
private:
vector<T> ts;
public:
O(vector<T> sourse)
:ts(sourse)
{
vector<T>::itereator it = ts.begin();
}
};
看上去没啥问题,可是一旦我们编译,就会
只是为什么呢?
因为vector<T>::itereator
这段语句内,因为vector<T>
是不确定的,所以我们的编译器无法确认所谓的iteratoe(注:图中的代码,单词打错了,但是不影响结果,望别介意)是vector<T>
的子类还是静态变量
注:子类这个说法是不准确的,详见stl源码
二义性是计算机系统难以接受的,所以报错。
至于为什么IDE不报错,又是另一个故事了
为了消除二义性
template<class T>
class O
{
private:
vector<T> ts;
public:
O(vector<T> sourse)
:ts(sourse)
{
typename vector<T>::iterator;
}
};
像我这样加上typename
就可以消除二义性,声明后面的iterator
是一个类型,而非一个变量。
没有提示,没有报错
像是我们上文的代码,如果你的IDE有自动补全的话,你就会发现ts.
这样的代码会触发自动补全而vector<T>
这样的代码就不行。
这可能有比较深的原因,但是挺坑的,就记一下吧
没有动态报错情况详见代码
对于模板函数或者模板类内部的代码,不会进行动态检验,因此使用vs写的时候,我们的体验就回归了vc++(==)。
至于原因,我咨询了大佬,大佬也不知道,那就凉凉喽
error type的原因
出现这种情况有两种可能
- 参数类型写错了,这是绝大多数的情况,因为vs的类型推导十分zz,再加上没有动态检验,就会出现类型推到错误,也就是说函数的第一个参数写错了,后面的参数以及函数体里的变量都会显示error-type
- 类的顺序错了,不说了,太zz了
分离编译
如果你恰好遇到了编译失败,原因是符号问题,那你可能是吃了分离编译的亏,因为,现在的编译器没有一个支持泛型的分离编译的。因为编译是对单个文件进行的,之后再进行链接。
error LNK2019: 无法解析的外部符号 "void __cdecl func< int>(int const &)" (??$func@H@@YAXABH@Z)
我们来复习一下模板的实现
- 程序员写出模板(例如模板函数)
- 程序员在别的地方确立对于模板的调用
- 编译器将模板的实例化调用符号化为符号
- 根据符号来生成对应符号化的模板函数
也就是说当编译过程一过,编译器将不会再去实例化函数
参考一位大佬的回答
原因出现在分离编译模式上。在分离编译模式下,func.cpp会生成一个目标文件为func.obj,由于在func.cpp文件中,并没有发生函数模板调用,所以不会将函数模板func< T>实例化为模板函数func< int>,也就是说,在func.obj中无法找到关于模板函数func< int>的实现代码。在源文件main.cpp中,虽然函数模板被调用,但由于没有模板代码,也不能将其实例化。也就是说,在main.obj中也找不到模板函数func< int>的实现代码。这样,在连接的时候就会出现func< int>没有定义的错误。
所以要把函数的定义和声明放在同一个文件
在同一个文件里进行类似调用也行,但是不推荐
和普通函数不同的是,这里不会报函数重定义的错,这是对于模板函数的特殊照顾
泛型的尖括号不补全
这问题请找罪魁祸首微软
静态函数成员的一些问题
当我们定义一些类的静态方法时,我们会很震惊的发现,vs把静态方法的声明和下面的定义视为两个符号,而定义的函数体内,会失去绝大多数高亮补全,或者报错。而且内部的符号在vs中全部是未定义状态。而编译器最后解析的时候都是一视同仁的。
解决方法
可以考虑将定义和声明放在一起~~,但是并没有解决实际问题23333~~
至于真正的解决方法,只有等vs更新了。
模板类内双重模板与auto的推导错误
假定有
template <typename T>
void ShowProcess(vector<vector<T>> elements, string tip)
{
cout << tip << endl;
for (auto line : elements)
{
for (auto element : line)
{
cout << setw(4) << element;
}
cout << endl;
}
cout << endl;
}
按照正常情况,这段代码一点问题都没有。
实际上这里会出现推导错误
line会被推导为T类型,然后雪崩
应该改为
template <typename T>
void ShowProcess(vector<vector<T>> elements, string tip)
{
cout << tip << endl;
for (vector<T> line : elements)
{
for (auto element : line)
{
cout << setw(4) << element;
}
cout << endl;
}
cout << endl;
}