C++编程思想(卷二):深入理解模板
2009-11-09 10:03
330 查看
每个模板参数描述了下述内容之一:
1.类型
2.编译时常数值
3.其他模板
默认模板参数:
在类模板中,可以为模板参数提供默认参数,但是在函数模板中却不行。作为默认的模板参数,它们只能被定义一次。
当声明一个实例时必须使用一对空的尖括号:Stack<> myStack;
默认参数大量用于标准C++库中。
tmplate<class T, class Allocator = allocator<T> > :注意最后两个右尖括号字符之间有空格
模板型模板参数的内部参数需要重复声明
typename关键字
当编译器看到typename T::id,就会明白id指的是一个嵌套类型,之后它就可以创建一个这个类型的对象了。
若一个模板代码内部的某个类型被模板类型参数所限定,则必须使用关键字typename作为前缀进行声明
typename的用法:
1.创建一个新类型:typename Seq<T>::iterator It;
2.用typename代替class:可以在模板定义的模板参数列表中选择用typename代替class
template<typename T> class X {};
以template关键字作为提示
成员模板函数不能被声明为virtual类型。
若一个函数模板的返回类型是一个独立的模板参数,当调用它的时候就一定要明确指定它的类型。
函数模板的半有序:特化程度最高的模板匹配
模板特化(specialization):
两次都调用了特化版本
当为一个类模板提供了一个完整特化时,依然需要实现其中的所有成员函数。
类模板支持半特化,而函数模板不支持,函数模板只能通过重载来代替半特化。
模板的引入导致了模板编译需要分两个阶段进行:
1.编译器解析模板定义,寻找明显的语法错误,还要对它所能解析的所有名称进行解析。
2.模板实例化。
关联查找:
若名称是关联的,则它的查找是在实例化时进行的,非限定关联名称除外,非限定关联名称是一个普通名称查找,它的查找进行的比较早,是在定义时进行。所有模板中的非关联名称被较早地查找,这种查找是在模板定义被解析的时候进行。
策略类可能是普通类,也可能是模板,还有可能是结合了使用继承机制全部优点的类。
奇特的递归模板模式:
模板元编程:
编译时编程
1.编译时循环
例:产生阶乘
2.循环分解
因为每个函数的代码可以内联,实际插入main()函数的代码就是一个单一的表达式m*m*m。
该方法可以完全避免循环控制顶层的出现。
3.编译时选择:
表达式模板:
当表达式中的变量极大时,产生的临时变量就会耗尽系统的资源。表达式模板允许在没有临时变量的情况下使用同一个表达式。
将完整定义的模板放在每个编译单元中的原因在于:
1.头文件中的非内联函数体会导致多函数的定义,从而导致链接错误。
2.隐藏来自客户有益的函数实现,从而减少了编译时连接。
3.商家可能将预编译代码分配到各个头文件中,从而使得用户看不到函数的具体实现。
4.头文件越小,编译时间就越短。
为了帮助减少包含模型所需要的大的头文件,C++提供了两个可供选择的代码组织机制:
1.显示实例化来手工地实例化每一个模板特化
2.使用导出模板,它支持最大限度的独立的编译。
为了手工实例化一个特定的模板特化,可以在该特化的声明前使用template关键字。
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关键字。
相关文章推荐
- C++编程思想(卷二):深入理解字符串
- C++编程思想(卷二):深入理解字符串
- 深入理解移动开发的模板复用机制
- 深入理解JavaScript系列(41):设计模式之模板方法
- JDBC模型—深入理解JDBC设计思想(探究Class.forName("DBDriver"))
- 深入理解Spring MVC 思想
- 深入理解Spring MVC 思想
- 深入理解EventBus的设计思想
- 深入理解JavaScript系列(41):设计模式之模板方法
- C++编程思想(第2卷)之深入探索string
- 深入理解Android内核设计思想 第三天 第五章
- 深入理解JavaScript系列(41):设计模式之模板方法
- 深入理解Spring MVC 思想
- 深入理解Android内核设计思想——读书笔记
- 深入理解Spring MVC 思想
- 深入理解Spring MVC 思想
- 深入理解Magento – 第三章 – Magento的布局(Layout),块(Block)和模板(Template).doc
- 深入理解面向过程与面向对象的思想差异与各自特色
- 深入理解SYN攻击.+防范思想及ROS设置
- 夯实java基础,深入理解Android设计思想