模板
C++另一种编程思想称为泛型编程,主要技术就是模板; C++提供两种模板机制:函数模板和类模板
函数模板
建立一个通用函数,其函数返回值类型和形参类型可以不用具体制定
用一个虚拟的类型来代表(类型参数化);
template<typename T>
函数声明或定义;
template -- 声明创建模板;
typename -- 表明其后面的符号是一种数据类型,可以用class代替
T -- 通用的数据类型,名称可以替换,通常为大写字母
template<typename T>
void swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
int main(){
int a = 10;
int b = 20;
swap(a, b);
swap<int>(a,b);
}
自动类型推导,必须推导出一致的数据类型T才可以使用;
模板必须要确定出T的数据类型,才可以使用;
普通函数和函数模板的区别
普通函数调用时可以发生自动类型转换(隐式类型转换)
函数模板调用时: **模板<数据类型>(…):**不能发生类型转换,必须为指定类型 **模板<>(…):**自动推断合适的模板,也不能发生类型转换
普通函数与函数模板的调用规则
void func(int a,int b){
cout << "普通函数" << endl;
}
template<typename T>
void func(T a,T b){
cout << "函数模板" << endl;
}
int main(){
int a = 10;
int b = 10;
func(a,b);
func<>(a,b);
}
模板的局限性
class A{
public:
A(int x,int y):a(x),b(y){}
int a;
int b;
};
template<typename T>
bool func(T &a,T &b){
if (a == b){
return true;
}
return false;
}
template<>bool func(A &a,A &b){
if (a.a == b.a && a.b == b.b){
return true;
}
return false;
}
int main(){
A a1(10,10);
A a2(20,10);
bool a = func(a1,a2);
}
控制实例化
extern template class Blod<string>;
template int compare(int&,int&);
重载与模板
可变参数模板
template<typename T,typename... Args>
void foo(const T& t, const Args& ... rest)
{
std::cout << sizeof...(Args) << std::endl;
std::cout << sizeof... (rest) << std::endl;
}
template<typename T>
std::ostream& print(std::ostream& os, const T& t)
{
return os << t;
}
template<typename T, typename... Args>
std::ostream& print(std::ostream& os, const T& t, const Args&...args)
{
os << t << ',';
return print(os, args...);
}
print(os, show(args)...)
print(os, show(a1),show(a2) ... show(an));
std::forward<Args>(args)...
std::forward都会按照原来的类型完美转发。
传进来之后有了变量名就变成了左值。
模板特例化
template<typename T>
int compare(const& T,const& T){...};
template<>
int compare(const char* const&,const char* const&){...};
如果有一个函数模板,它的模板参数既作为参数类型又作为返回类型,那么一定要首先声明函数的返回类型参数,否则就不能省略调用函数参数表中的任何类型的参数。
template<typename R,typename P >
R implicit_cast(const P& p)
{
return p;
}
int main()
{
int i = 1;
float x = implicit_cast<float>(i);
return 0;
}
template<typename P,typename R >
R implicit_cast(const P& p)
{
return p;
}
int i = 1;
float x = implicit_cast<int, float>(i);
类模板
template<class NameType,class AgeType>
class A{
public:
A(NameType name,AgeType age){
this->_name = name;
this->_age = age;
}
NameType _name;
AgeType _age;
};
int main(){
A<string,int> a("zxl",18);
}
类模板与函数模板的区别
template<class NameType,class AgeType = int>
class A{
public:
A(NameType name,AgeType age){
this->_name = name;
this->_age = age;
}
NameType _name;
AgeType _age;
};
int main(){
A<string, int> a("zxl",100);
A<string> a("zxl",100);
}
类模板中成员函数的创建时机
普通类中的成员函数一开始就可以创建 类模板中的成员函数在调用时才创建
类模板对象做函数参数
指定传入的类型 – 直接显示对象的数据类型 参数模板化 – 将对象中的参数变为模板进行传递 整个类模板化 – 将这个对象类型模板化进行传递
template<class T1,class T2>
class A{
public:
A(T1 name,T2 age){
this->_name = name;
this->_age = age;
}
T1 _name;
T2 _age;
};
void func(A<string,int>&p);
template<class T1,class T2>
void func(A<T1,T2>&p);
template<class T>
void func( T );
int main(){
A<string,int> a("zxl",100);
}
成员模板
成员模板不能是虚函数
template<typename T>
class A
{
template<typename U>
A(U b,U e);
}
template<typename T>
template<typename U>
A<T>::A(U b, U e){};
类模板与继承
当子类继承的父类是一个类模板时,子类在声明的时候,要指出父类中T的类型 如果不指定,编译器无法给子类分配内存 如果想灵活指定出父类中T的类型,子类也需要为类模板
template<class T>
class Base{
T m;
};
class Son:public Base<int>{
};
template<class T,class T1>
class Son1:public Base<T>{
T1 b;
}
类模板成员函数类外实现
需要加上模板 成员模板不能是虚函数
template<class T1,class T2>
class A{
public:
A(T1 name, T2 age);
void func();
T1 _name;
T2 _age
};
template<class T1,class T2>
A<T1,T2>::A(T1 name,T2 age){
}
template<class T1,class T1>
void A<T1,T2>::func(){
}
类模板分文件编写
问题 类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到 解决 1.直接包含.cpp源文件; 2.将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定名称,并不是强制的; 主流的解决方法是用第二种
类模板与友元
全局函数内类实现 – 直接内类声明 全局函数内外实现 – 特殊方法 一般而言没有特殊需求的化就用类内实现
template<class T1,class T2>
class A;
template<class T1,class T2>
void func1(A<T1,T2> p)
{
cout << "类外" << endl;
}
template<class T1,class T2>
class A
{
friend void func(A<T1,T2> p)
{
cout << "类内" << endl;
}
friend void func1<>(A<T1,T2> p);
public:
A(T1 name, T2 age){
this->_name = name;
this->_age = age;
}
private:
T1 _name;
T2 _age
};
例子
#pragma once
#include<iostream>
template<class T>
class Arr {
public:
Arr(int cap) {
this->_cap = cap;
this->_size = 0;
this->padd = new T[this->_cap];
}
Arr(const Arr& arr) {
this->_cap = arr._cap;
this->_size = arr._size;
this->padd = new T[arr._cap];
for (int i = 0; i < this->_size; i++) {
this->padd[i] = arr.padd[i];
}
}
Arr& operator=(const Arr& arr) {
if (this->padd != NULL) {
delete[] this->padd;
this->padd = NULL;
this->_cap = 0;
this->_size = 0;
}
this->_cap = arr._cap;
this->_size = arr._size;
this->padd = new T[arr._cap];
for (int i = 0; i < this->_size; i++) {
this->padd[i] = arr.padd[i];
}
return *this;
}
void push_back(const T& val) {
if (this->_cap == this->_size) { return; }
this->padd[this->_size] = val;
this->_size++;
}
void pop_back() {
if (this->_size == 0) { return; }
this->_size--;
}
T& operator[](int index) {
return this->padd[index];
}
~Arr()
{
if (this->padd != NULL) {
delete[] this->padd;
this->padd = NULL;
}
}
private:
T* padd;
int _cap;
int _size;
};
非类型模板参数
template<unsigned N,unsigned M>
int compare(const char(&p1)[N], const char(&p2)[M])
{
return strcmp(p1, p2);
}
模板编译
当编译器遇到一个模板定义时,它并不生产代码 只有当我们实例化出模板的一个特定版本时,编译器才生成代码
编译时编程
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;
return 0;
}
正如将进行类型参数代替作为一种方便的方法,这意味着产生了一种支持编译时编程的机制。这样的程序成为模板元程序(template metaprogram)。
实际上,模板元编程就是完全的图灵机(Turing complete),因为它支持选择(if-else)和循环(通过递归)。从理论上讲,可以用它执行任何的计算
template<int n>
struct Fib
{
enum
{
val = Fib<n-1>::val + Fib<n-2>::val
};
};
template<>
struct Fib<1>
{
enum
{
val = 1
};
};
template<>
struct Fib<0>
{
enum
{
val = 0
};
};
int main()
{
cout << Fib<20>::val << endl;
return 0;
}
表达式模板
表达式模板能够使某些计算得到全方面的编译时优化,表达式模板允许在没有临时变量的情况下使用同一个表达式。 例如:将三个个矩阵或向量相加 D = A + B + C;
这个表达式将会导致一些临时变量的产生,一个是A+B,一个是(A+B)+ C。当这些变量代表极大的矩阵或者向量时,资源的消耗时难以接受的。
#include<cstddef>
#include<cstdlib>
#include<ctime>
#include<iostream>
using namespace std;
template<typename, size_t>
class MyVectorSum;
template<typename T, size_t N>
class MyVector
{
private:
T data[N];
public:
MyVector<T, N>& operator=(const MyVector<T, N>& right)
{
for (size_t i = 0; i < N; ++i)
{
data[i] = right.data[i];
}
return *this;
}
MyVector<T, N>& operator=(const MyVectorSum<T, N>& right);
const T& operator[](size_t i) const
{
return data[i];
}
T& operator[](size_t i)
{
return data[i];
}
};
template<typename T,size_t N>
class MyVectorSum
{
private:
const MyVector<T, N>& left;
const MyVector<T, N>& right;
public:
MyVectorSum(const MyVector<T, N>& lhs,
const MyVector<T, N>& rhs)
: left(lhs), right(rhs) {};
T operator[](size_t i) const
{
return left[i] + right[i];
}
};
template<typename T,size_t N>
MyVector<T, N>& MyVector<T, N>::operator=(const MyVectorSum<T, N>& right)
{
for (size_t i = 0; i < N; ++i)
{
data[i] = right[i];
}
return *this;
}
template<typename T,size_t N>
inline MyVectorSum<T, N> operator+(const MyVector<T, N>& left,
const MyVector<T, N>& right)
{
return MyVectorSum<T, N>(left, right);
}
template<typename T,size_t N>
void print(MyVector<T, N>& v)
{
for (size_t i = 0; i < N; ++i)
{
cout << v[i] << ' ';
}
cout << endl;
}
template<typename T,size_t N>
void init(MyVector<T, N>& v)
{
for (size_t i = 0; i < N; ++i)
{
v[i] = rand() % 100;
}
}
int main()
{
srand(time(0));
MyVector<int, 5> v1;
init(v1);
print(v1);
MyVector<int, 5> v2;
init(v2);
print(v2);
MyVector<int, 5> v3;
v3 = v1 + v2;
print(v3);
}
当MyVectorSum类产生时,它并不进行计算;它只是持有两个待加向量的引用,仅当访问一个向量和的成员时,计算才会发生 但这样并不支持多操作,例如:v4 = v1 + v2 + v3 没有定义MyVectorSum + MyVector的操作 可以通过模板解决
|