一、模板应用的几种情况
在模板的开发中,经常会遇到一些比较少见的用法,特别是本身对模板开发应用比较少的,可能看到这些用法头就有些蒙圈。这种情况在模板嵌套中比较常见,本篇就是把这些比较少见的用法分析一下,结合前面的一篇“跟我学c++中级篇——模板的模板参数和成员模板”一起学习,会有更好的领悟。
二、几种符号的在模板依赖限定中的作用
在一些模板的源码中可以看到类似“->template”、“::template”、“.template”用法,比如“DW::template Write((typename U::DataType)v)”,直观上第一印象会是,这三个运算符“->,.和 ::”不是限定域的么?对,它就是做限定的,在模板中的应用也是如此。明白这一点很重要,正如在前面学习重载operator运算时,提到过的一句话,“重载运算符的意义尽量要和运算符本身保持一致”,举一个例子,重载了一个“+”加法运算符,却是用的乘法规则,这就不好。但是,从语法意义上又没有问题,就是这个意思。
三、依赖限定符的说明
先看一个<C++ Templates The Complete Guide> 2th中( 13.3.3节),一个例程:
template<typename T>
class Shell {
public:
template<int N>
class In {
public:
template<int M>
class Deep {
public:
virtual void f();
};
};
};
template<typename T, int N>
class Weird {
public:
void case1 (
typename Shell<T>::template In<N>::template Deep<N>* p) {
p->template Deep<N>::f(); // inhibit virtual call
}
void case2 (
typename Shell<T>::template In<N>::template Deep<N>& p) {
p.template Deep<N>::f(); // inhibit virtual call
}
};
“p->template Deep::f()”,用到了“->”后面还有一个“p.template”,而“typename Shell::template In::template Deep”又出现了“::template”(说明,typename表示::后的为类型,详情可参考相关书籍),它和前在的例子,“DW::template Write((typename U::DataType)v)”都是类和函数限定的方式。
当此类限定符前面的名称或者表达式类型依赖于模板参数,可能会引起未知的特化,并且此时限定符后的名称为模板标识时,就会使用上述的方式来进行依赖限定说明。
在上面的这个例子中,p类型依赖于模板参数T(typename Shell ==》 template<typename T, int N>),编译器无法确定Deep是个模板类,所以需要使用template这个关键字来显示的指示出依赖限定。否则编译器就会将其解析为“((p.Deep)<n)>f()”。这和上面小括号中对typename的限定增加是一样的,前者确定是类型,后者告诉编译器是模板。
再看一个函数的例子:
#include <iostream>
void run() {
std::cout << "call function run()" << std::endl;
}
template<typename T>
class Example {
public:
template <int N = 10>
void run() {
std::cout << "Example member function run(): " << N << std::endl;
}
};
template<typename T>
class Sub : public Example<T>
{
public:
Sub(T t) :mt() {}
void callrun0() { run(); } // 全局 f()
void callrun1() { /*this->template run();*/ this->run(); } //注释为G++,默认为VC
void callrun2() { Example<T>::template run(); }
void callrun3() { Example<T>::template run<3>(); }
void callrun4() { ::run(); }
private:
T mt;
};
int Test()
{
Sub<int> s(7);
s.callrun0();
s.callrun1();
s.callrun2();
s.callrun3();
s.callrun4();
return 0;
}
这里是一个函数限定的应用,对比着一看就更明白了。
四、总结
“遇到问题不要慌,先拍个照,发个朋友圈”。遇到问题时,需要冷静的联想相关的知识,然后再有针对性的查找相关资料。说一句不太准确的话,在大多数人的认知体系中,几乎所有的知识都是有传承体系的,不会突然出现,遇到一些不太清楚的用法,也不要着急,因为要明白,学习的知识是已有的,而不是需要你去创造的。否则,就是发明家了。学习还是有技巧有方法可以掌握的。
好好学习,天天向上。