您的位置:首页 > 编程语言 > C语言/C++

C++编程思想(卷二):深入理解模板

2009-11-09 10:03 330 查看
每个模板参数描述了下述内容之一:

1.类型

2.编译时常数值

3.其他模板

默认模板参数:

在类模板中,可以为模板参数提供默认参数,但是在函数模板中却不行。作为默认的模板参数,它们只能被定义一次。

当声明一个实例时必须使用一对空的尖括号:Stack<> myStack;

默认参数大量用于标准C++库中。

tmplate<class T, class Allocator = allocator<T> > :注意最后两个右尖括号字符之间有空格

模板型模板参数的内部参数需要重复声明

typename关键字

template<class T> class X {
typename T::id i;	  //id被当作T内的一个嵌套类型处理
public:
void f() { i.g(); }
};
class Y {
public:
class id {
public:
void g() {}
};
};
int main() {
X<Y> xy;
xy.f();
}


当编译器看到typename T::id,就会明白id指的是一个嵌套类型,之后它就可以创建一个这个类型的对象了。

若一个模板代码内部的某个类型被模板类型参数所限定,则必须使用关键字typename作为前缀进行声明

typename的用法:

1.创建一个新类型:typename Seq<T>::iterator It;

2.用typename代替class:可以在模板定义的模板参数列表中选择用typename代替class

template<typename T> class X {};

以template关键字作为提示

template<class charT, size_t N>
basic_string<charT> bitsetToString(const bitset<N>& bs) {
return bs. template to_string<charT, char_traits<charT>,	//告诉编译器紧接着的是一个模板名称,<字符才能被正确解释
allocator<charT> >();
}


成员模板函数不能被声明为virtual类型。

若一个函数模板的返回类型是一个独立的模板参数,当调用它的时候就一定要明确指定它的类型。

函数模板的半有序:特化程度最高的模板匹配

模板特化(specialization):

#include <cstring>
#include <iostream>
using std::strcmp;
using std::cout;
using std::endl;
template<class T> const T& min(const T& a, const T& b) {
return (a < b) ? a : b;
}
// An explicit specialization of the min template
template<>							//告诉编译器接下来的是一个模板的特化
const char* const& min<const char*>(const char* const& a,	//模板特化的类型必须出现在函数名后紧跟的尖括号中
const char* const& b) {
return (strcmp(a, b) < 0) ? a : b;
}
int main() {
const char *s2 = "say /"Ni-!/"", *s1 = "knights who";
cout << min(s1, s2) << endl;
cout << min<>(s1, s2) << endl;
}


两次都调用了特化版本

当为一个类模板提供了一个完整特化时,依然需要实现其中的所有成员函数。

类模板支持半特化,而函数模板不支持,函数模板只能通过重载来代替半特化。

模板的引入导致了模板编译需要分两个阶段进行:

1.编译器解析模板定义,寻找明显的语法错误,还要对它所能解析的所有名称进行解析。

2.模板实例化。

关联查找:

若名称是关联的,则它的查找是在实例化时进行的,非限定关联名称除外,非限定关联名称是一个普通名称查找,它的查找进行的比较早,是在定义时进行。所有模板中的非关联名称被较早地查找,这种查找是在模板定义被解析的时候进行。

策略类可能是普通类,也可能是模板,还有可能是结合了使用继承机制全部优点的类。

奇特的递归模板模式:

#include <iostream>
using namespace std;
template<class T> class Counted {
static int count;
public:
Counted() { ++count; }
Counted(const Counted<T>&) { ++count; }
~Counted() { --count; }
static int getCount() { return count; }
};
template<class T> int Counted<T>::count = 0;
// Curious class definitions
class CountedClass : public Counted<CountedClass> {};
class CountedClass2 : public Counted<CountedClass2> {};
int main() {
CountedClass a;
cout << CountedClass::getCount() << endl;    // 1
CountedClass b;
cout << CountedClass::getCount() << endl;    // 2
CountedClass2 c;
cout << CountedClass2::getCount() << endl;   // 1 (!)
}


模板元编程:

编译时编程

1.编译时循环

例:产生阶乘

#include <iostream>
using namespace std;
template<int n> struct Factorial {
enum { val = Factorial<n-1>::val * n };
};
template<> struct Factorial<0> {
enum { val = 1 };
};
int main() {
cout << Factorial<12>::val << endl; // 479001600
}


2.循环分解

#include <iostream>
using namespace std;
template<int n> inline int power(int m) {
return power<n-1>(m) * m;
}
template<> inline int power<1>(int m) {
return m;
}
template<> inline int power<0>(int m) {
return 1;
}
int main() {
int m = 4;
cout << power<3>(m) << endl;
}


因为每个函数的代码可以内联,实际插入main()函数的代码就是一个单一的表达式m*m*m。

该方法可以完全避免循环控制顶层的出现。

3.编译时选择:

#include <iostream>
using namespace std;
template<bool cond> struct Select {};
template<> class Select<true> {
static void statement1() {
cout << "This is statement1 executing/n";
}
public:
static void f() { statement1(); }
};
template<> class Select<false> {
static void statement2() {
cout << "This is statement2 executing/n";
}
public:
static void f() { statement2(); }
};
template<bool cond> void execute() {
Select<cond>::f();	//在运行时执行
}
int main() {
execute<sizeof(int) == 4>();
}


表达式模板:

当表达式中的变量极大时,产生的临时变量就会耗尽系统的资源。表达式模板允许在没有临时变量的情况下使用同一个表达式。

将完整定义的模板放在每个编译单元中的原因在于:

1.头文件中的非内联函数体会导致多函数的定义,从而导致链接错误。

2.隐藏来自客户有益的函数实现,从而减少了编译时连接。

3.商家可能将预编译代码分配到各个头文件中,从而使得用户看不到函数的具体实现。

4.头文件越小,编译时间就越短。

为了帮助减少包含模型所需要的大的头文件,C++提供了两个可供选择的代码组织机制:

1.显示实例化来手工地实例化每一个模板特化

2.使用导出模板,它支持最大限度的独立的编译。

为了手工实例化一个特定的模板特化,可以在该特化的声明前使用template关键字。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: