Template parameters有三种类型:
- 类型参数Type parameters
- 非类型参数 Non-type Parameters
- 双重模板参数 Template Template Parameters
所谓模板参数template parameters是在模板声明语句的参数化子句(parameterized clause)中声明的变量。Template parameters不一定要署名:
template <typename , int > // 这里,两个模板参数都没有名称
class X;
但是当template程序中需要用到某个template parameter时,后者就必须署名。另外,后面声明的template parameters可以用到先声明的template parameters的名称,反之不然:
template <typename T, //第一个参数T,被用于第二和第三参数的声明中
T* Root,
template<T*> class Buf > //Buf, 就是template template parameter
class Struture;
1. 类型参数Type Parameters
Type parameters可以采用关键词typename 或关键词class导入,两者等价。其声明形式是:关键词typename 或class后面跟一个简单标识符,该符号后面可跟一个逗号以便区隔下一个参数,也可以使用闭锁角括号结束参数子句,或跟一个等号=标识出预设模板自变量default template argument。
在template 声明语句中type parameter的作用非常类似typedef的名称。例如不能使用如class T这样的完整名称,即使T确实表示一个class type:
template <typename Allocator>
class List {
class Allocator* allocator;//Error
friend class Allocator; //Error
...
};
2. 非类型参数NonType Parameters
Nontype template parameter是指那些可在编译期或链接期确定其值的常数。此种参数的类型必须是以下三者之一:
- 整数integral或列举enumeration类型
- pointer类型:包括指向常规objects、指向functions和指向members
- referenc类型:包括指向objects或指向functions。
令人惊讶的是,nontype parameter的声明在某种情况下也以关键词typename为前缀词
template <typename T, //type parameter
typename T::Allocator* Allocator> //nontype parameter
class List;
Nontype parameters 也可以是functions类型或array类型,但它们都会退化为对应的pointer类型:
template <int buf[5]> class Lexer;
template <int* buf> class Lexer;
Nontype template parameters的声明类似变量声明,但不能加上static、mutable之类的修饰。你可以加上const或volatile,但如果修饰词出现在参数类型的最外层,编译器会忽略它们:
template <int const length> class Buffer;
template <int length> class Buffer; //与上一行等价
最后一点,nontype parameters总是右值rvalues:它们不能被取值,也不能被赋值,
3. 双重模板参数Template Template Parameter
template template parameters是一种class template占位符,其声明方式和class template类似,只是不能使用关键词struct和union:
template <template <typename X> class C> //OK
void f(C<int>* p);
template <template<typename X> struct C> //Error
void f(C<int>* p);
template <template<typename X> union C> //Error
void f(C<int>* p);
在它们的作用域内,你可以像使用class template那样地使用template template parameters。
1)template template parameters的参数也可以default template arguments
如果客户端没有为响应的参数指定自变量,编译器就会使用这些预设自变量:
template <template <typename T,
typename A = MyAllocator> class Container>
class Adaptation {
Container<int> storage; //等价于 Container<int, MyAllocator>
...
};
2)template template parameters中,template parameter的名称只能被用于template template parameter 的其他参数声明中。
template <template<typename T, T*> class Buf>
class Lexer {
static char storage[5];
Buf<char, &Lexer<Buf>::storage[0]> buf;
...
};
template <template<typename T> class List>
class Node {
static T* storage;
...
};
3)通常template template parameter中的template parameters名称并不会在其他地方用到,因此,未被用到的template parameter可以不署名。
例如,上面的Adaption template可被声明为:
template <template> <typename,
typename = MyAllocator> class Container>
class Adaptation
{
Container<int> storage; //等价于Container<int, MyAllocator>
...
}
4. 预设的模板引数Default Template Arguments
目前,只有class template的声明语句可以存在default template argument。无论何种template parameter都可以有预设的自变量,当然它必须匹配对应参数。很明显,预设自变量不能相依于其自身参数,但可以相依于之前声明的参数:
template <typename T, typename Allocator = allocator<T> >
class List;
和函数的预设自变量一样,某个参数(不论是调用参数还是模板参数)带有预设自变量的条件是:其后续所有参数也都有预设自变量。
后续参数的预设自变量通常写在同一个template声明语句中,但也可以写在该template更早的某个声明语句中。例如,
template <typename T1, typename T2, typename T3,
typename T4= char , typename T5 = char>
class Quintuple; //OK
template <typename T1, typename T2, typename T3 = char,
typename T4, typename T5>
class Quintuple; //OK: T4, T5先前已经有默认值
template <typename T1=char, typename T2, typename T3,
typename T4, typename T5>
class Quintuple; //错误:T1不能有默认值,因为T2没有默认值
此外,我们也不能重复指定default template arguments:
template <typename T = void>
class Value;
template <typename T= void > //Error:预设自变量重复
class Value;