模板初阶
一、函数模板
函数模板概念:
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生 函数的特定类型版本。
函数模板格式:
template <typename X1,typename X2…………typename Xn> //typename可以用class代替
函数模板原理:
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。 所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
template <typename t>void swap(t& x, t& y)
{t a = x;x = y;y = a;
}
上面实现的swap函数就是用了函数模板,当我们调用函数时,编译器会自动推导形参的类型,然后再生成具体函数。
函数模板实例化
函数模板实例化分为隐式实例化和显示实例化
隐式实例化:
隐式实例化:让编译器根据实参推演模板参数的实际类型
template <typename t>void Swap(t& x, t& y)
{t a = x;x = y;y = a;
}int main()
{int a = 1, b = 2;double c = 3.1, d = 4.2;Swap(a, b);Swap(c, d);return 0;
}
当然还会产生这样的问题:
Swap(a,c);
因为模板列表中只有一个t,这样写的话就不清楚t是int还是double类型从而报错。要处理的话可以选择类型强制转化或者模板显式实例化。
Swap((double)a,c);//类型强制转化
值得注意的是,类型强制转化的过程中会产生临时对象,而临时对象具有常性,故函数形参必须不可修改。
因为Swap函数的特殊性实参不能直接强转类型,故下面暂时替换成add函数。
template <typename t>//const引用接收临时对象
t add(const t& x, const t& y)
{return x+y;
}int main()
{int a = 1, b = 2;double c = 3.1, d = 4.2;add((double)a,c);//类型强制转化产生临时对象,临时对象具有常性return 0;
}
显示实例化
显式实例化:在函数名后的<>中指定模板参数的实际类型
Swap<int>(a,c);//将函数参数显示化为int类型
当然如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。
std::string s="fsad";Swap<int>(s,c);//s为string类型无法隐式类型转化成int类型,编译报错
模板参数的匹配原则
1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这 个非模板函数
2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而 不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
int Swap(int& x, int& y)
{int a = x;x = y;y = a;return 0;
}template <typename t>
void Swap( t& x, t& y)
{t a = x;x = y;y = a;
}int main()
{int a = 1, b = 2;double c = 3.1, d = 4.2;Swap(a, b);//a,b均为int类型,不需要调用模板Swap(c, d);//没有符合的函数,调用模板return 0;
}
3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
二、类模板
类模板的定义格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};
类模板实例化:
template <typename t>
class TN
{
public:TN(t y):x(y){}
private:t x;//…………
};
//显示实例化TN<double>
template class TN<double>;int main()
{TN<int> a1(10);//隐式实现,<int>只是指出10为int类型,并没有直接生成具体的类TN<double> a2(20) ;//显示实例化return 0;
}
上面类模板隐式实例化中:TN是类名,TN<类型名> 才是类型。
类模板显式实例化与隐式实例化的不同一点就是显式实例化还需要一个专门的语法:
template class 类名<实例化的类型>;
注意事项:
模版不建议声明和定义分离到两个文件.h 和.cpp,会出现链接错误。模板不是直接编译成可执行代码的实体,而是代码生成的蓝图。编译器只有在同时看到模板的完整定义和具体实例化类型时,才能生成对应的具体代码。
当进行声明与定义分离时,编译器会因为声明和显式实例化不完整从而最终链接时,会出现 “未定义的引用” 错误