C++模板元编程(三)
C++模板元编程(三)
typename和template关键字
正确的使用关键字typename与template可以很多细微的语法错误,总结如下。
1. template关键字用来引入模板声明和定义,如:
template <class T> class Vector;
2. typename关键字通常用来取代class来声明模板类型的参数,如:
template <typename T> class Vector;
3. typename关键字和class关键字在声明模板类型参数时,一般情况下等价(区别见后面)。但有时选用其中某个关键字,在概念上更加明确,如:
// 模板参数中,第一个是声明一个type参数,第二个是一个类型为T::value_type的值。
// 可见第二种方式,概念更明确,虽然两种模板声明都可以。
template<typneame T, typename T::value_type> class Test;
template<class T, typename T::value_type> class Test;
4. 当一个依赖名表示的是一个依赖性的类型时,需要使用typename关键字(对非依赖名,是否使用typename关键字对其进行限定完全是可选的),如:
template<typename Tp>
class IteratorTraits
{
public:
// 当使用一个依赖性的类型,且该名字表示一个类型时,C++标准要求使用typename关键字表明该依赖名是一个类型
typedef typename Tp::value_type value_type;
typedef typename Tp::reference reference;
typedef typename Tp::pointer pointer;
typedef typename Tp::difference_type difference_type;
typedef typename Tp::iterator_category iterator_category;
};
template<typename T>
class Test
{
public:
typename std::vector<T> v; // typename是可选的
// typedef typename int value_type; //(错误) typename不能用在非限定性(也就是不带::前缀)上
};
// typedef typename int value; // (错误)typename不能使用在模板之外的任何场合
// typename不能用于基类的名字上,即便它是依赖性的也不行
template<class T> struct Base;
template<class T> struct Derived
: typename Base<T>::type // (错误)
{};
// typename不能用于友元声明中
template<class T> class Test
{
friend class typename T::type;
};
5. 当一个依赖名是成员模板时,需要使用template消除歧义,如:
template<class T>
int f(T& x)
{
// x.convert成员函数模板,需要使用template关键字。不然的话可能会将convert当做数据成员,
// 而解析为(x.convert < 3) > pi, 从而造成歧义
return x.template convert<3>(Pi)
}
// 注:template禁止用在模板之外的任何场合,包括显示(完全)模板特化。也不能出现在using声明中
注意事项
模板函数中,参数类型不允许自动类型转换。
当存在同名的模板函数和非模板函数时,优先调用模板函数。但可以使用空的模板参数列表的形式,从template具现体中挑选适当的对象。
确保所有形式的重载函数都写在它们的调用点之前。
以模板类
template <typename T> Test{...}
申明变量和函数时都应该写成Test,然而只需要class名称而不需要class类型时,
只需写Test即可。如构造函数、析构函数的什么就属于这种情况。在模板类中的模板成员函数,只有被实际调用到了,才会被实例化。
模板的特化需以
template<>
开头。在模板类的模板参数中,可以指定模板参数的默认值
template<typename T, typename U=int>
。模板类的模板参数可以指定为非类型模板参数,同时也可以指定非类型函数模板参数。
模板的非类型参数只能是整型、enum、外部链接的指针,而不能是浮点类型、类类型对象与内部链接的指针。(在g++ 4.9.1上测试过)
模板也可以作为模板参数。
模板的声明与定义要放在同一个文件中。(也可以不放在同一个文件中,使用显示实例化)
显示实例化,以
template
打头,模板参数被完全替代。成员函数模板不能声明成virtual。
在同一作用域空间内,类模板不能和其它不同类型的实体共用同一个名称,而非模板类则可以和其它不同实体有相同的名称。
模板函数不能使用C链接方式(export “C”)。
模板通常使用外部链接,唯一例外的是static namespace scope 函数模板。
双重模板参数的声明不能使用struct和union,只能使用class。
template<template<typename T> class C> ...
模板的实例化(instantiation)是由泛化的模板定义式产生出实际函数和类型的过程。
Generic Programming, Traits, Policy classes, meta-programming, Expression Template。
Trait表现为一个template parameter的自然附加属性。 Policies表现泛化函数和类型间的可设置行为。
参考资料
- 《C++模板元编程》(David Abrahams, Aleksey Gurtovoy )
- 《C++ 模板全览》丁志强译