C++ primer 摘要《函数模板》20090120

===10.1 函数模板定义===
模板参数可以是一个模板类型参数,它代表了一种类型
也可以是一个模板非类型参数,它代表了一个常量表达式
template <class Type, int size> //当函数模板min()被实例化时size 的值会被一个编译时刻已知的常量值代替
Type min( Type (&arr) [size] );

 

// size 指定数组参数的大小并初始化一个 const int 值
template <class Type, int size>
Type min( const Type (&r_array)[size] )
{
    const int loc_size = size;
    Type loc_array[loc_size];
    // ...
}
注意项:
1、如果在全局域中声明了与模板参数同名的对象、函数或类型,则该全局名将被隐藏
typedef double Type;
template <class Type>
Type min( Type a, Type b )
{
    // tmp 类型为模板参数 Type
    // 不是全局 typedef
    Type tmp = a < b ? a : b;
    return tmp;
}
2、在函数模板定义中声明的对象或类型不能与模板参数同名
template <class Type>
Type min( Type a, Type b )
{
    // 错误: 重新声明模板参数 Type
    typedef double Type;
    Type tmp = a < b ? a : b;
    return tmp;
}
3、模板参数名在同一模板参数表中只能被使用一次例如下面代码就有编译错误
// 错误: 模板参数名 Type 的非法重复使用
template <class Type, class Type>
Type min( Type, Type );

4、一个模板的定义和多个声明所使用的模板参数名无需相同
例如下列三个min()的声明都指向同一个函数模板
// 三个 min() 的声明都指向同一个函数模板
// 模板的前向声明
template <class T> T min( T, T );
template <class U> U min( U, U );
// 模板的真正定义
template <class Type>
Type min( Type a, Type b ) { /* ... */ }

5、如果一个函数模板有一个以上的模板类型参数,则每个模板类型参数前面都必须有关键字class 或typename
// ok: 关键字 typename 和 class 可以混用
template <typename T, class U>
T minus( T*, U );

// 错误: 必须是 <typename T, class U> 或 <typename T, typename U>
template <typename T, U>
T sum( T*, U );

6、函数模板也可以被声明为inline 或extern
应该把指示符放在模板参数表后面,而不是在关键字template 前面
// ok: 关键字跟在模板参数表之后
template <typename Type>
inline
Type min( Type, Type );
// 错误: inline 指示符放置的位置错误
inline
template <typename Type>
Type min( Array<Type>, int );

  

模板实参推演
当函数模板被调用时,通过对函数实参类型的检查来决定模板实参的类型和值的过程。
要想成功地进行模板实参推演,函数实参的类型不一定要严格匹配相应函数参数的类型。
下列三种类型转换是允许的:左值转换,限定转换和到一个基类的转换
1、左值转换:包括从左值到右值的转换、从数组到指针的转换 或 从函数到指针的转换
int ai[4] = { 12, 8, 73, 45 };
int main() {
    int size = sizeof (ai) / sizeof (ai[0]);
    // ok: 从数组到指针的转换
    min2( ai, size );
}


2、限定转换:
template <class Type>
// 第一个参数是 const Type*
Type min3( const Type* array, int size ) {
    // ...
}
int *pi = ai;
// ok: 到 const int* 的限定修饰转换,函数实参在模板实参被推演之前,就先被转换为const Type*型
int i = min3( pi, 4 );

 

 3、到一个基类的转换
template <class Type>
class Array { /* ... */ };

template <class Type>
class ArrayRC : public Array<Type> { /* ... */ };

template <class Type>
Type min4( Array<Type>& array )
{
    Type min_val = array[0];
    for ( int i = 1; i < array.size(); ++i )
    if ( array[i] < min_val )
    min_val = array[i];
    return min_val;
}

int main() {
    //函数实参ArrayRC<int>在模板实参被推演之前,首先被转换成Array<int>型,然后Type的模板实参再被推演为int
    //被实例化的函数模板是min4(Array<int>&)
    ArrayRC<int> ia_rc( ia, sizeof(ia)/sizeof(int) );
    min4( ia_rc );
}

 

如果模板参数在函数参数表中出现多次,则每个推演出来的类型都必须与根据模板实参推演出来的第一个类型完全匹配
template <class T> T min5( T, T ) { /* ... */ }
unsigned int ui;
int main() {
    // 错误: 不能实例化 min5( unsigned int, int )
    // 必须是: min( unsigned int, unsigned int ) 或
    // min( int, int )
    min5( ui, 1024 );
}                                 
解决上述问题的办法是显示模板实参

===10.4 显式模板实参===
// min5( unsigned int, unsigned int ) 被实例化,1024通过有序标准转换被转换成类型unsigned int,如果隐式的就不会转换
min5< unsigned int >( ui, 1024 );

// T1 不出现在函数模板参数表中
template <class T1, class T2, class T3>
T1 sum( T2, T3 );

ui_type calc( char ch, ui_type ui )
{
    // 错误: T1 不能被推演出来
    ui_type loc1 = sum( ch, ui );
}
// ok: T3 是 unsigned int
// T3 从 ui 的类型中推演出来
ui_type loc3 = sum< ui_type, char >( ch, ui );
// ok: T2 是 char, T3 是 unsigned int
// T2 和 T3 从 pf 的类型中推演出来
ui_type (*pf)( char, ui_type ) = &sum< ui_type >;

===10.5 模板编译模式===
C++模板编译模式:包含模式Inclusion Model 和分离模式Separation Model
包含模式: 在每个模板被实例化的文件中包含函数模板的定义
分离模式: 函数模板的声明被放在头文件中,在定义的文件中使用关键字export,注意并不是所有的编译器都支持分离模式

 

你可能感兴趣的:(C++,c,UI,Class,编译器)